Skip to content

Commit

Permalink
Add SkipBloatUnderThresholdPredicate
Browse files Browse the repository at this point in the history
  • Loading branch information
mfvanek committed Nov 12, 2024
1 parent eba2fc6 commit a18edeb
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ public class IndexWithSize extends Index implements IndexSizeAware {

private final long indexSizeInBytes;

/**
* Constructs an {@code IndexWithSize} object with the specified table name, index name, and index size.
*
* @param tableName name of the table associated with the index; must be non-blank.
* @param indexName name of the index; must be non-blank.
* @param indexSizeInBytes size of the index in bytes; must be non-negative.
*/
@SuppressWarnings("WeakerAccess")
protected IndexWithSize(@Nonnull final String tableName,
@Nonnull final String indexName,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright (c) 2019-2024. Ivan Vakhrushev and others.
* https://github.com/mfvanek/pg-index-health
*
* This file is a part of "pg-index-health" - a Java library for
* analyzing and maintaining indexes health in PostgreSQL databases.
*
* Licensed under the Apache License 2.0
*/

package io.github.mfvanek.pg.model.predicates;

import io.github.mfvanek.pg.model.BloatAware;
import io.github.mfvanek.pg.model.DbObject;
import io.github.mfvanek.pg.model.validation.Validators;

import java.util.function.Predicate;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.ThreadSafe;

/**
* A predicate that filters out database objects with bloat values under specified thresholds.
* Only database objects that implement {@link BloatAware} are evaluated, and they will be skipped if
* their bloat size or percentage falls below the specified thresholds.
*
* @author Ivan Vakhrushev
* @see Predicate
* @see BloatAware
* @since 0.13.3
*/
@Immutable
@ThreadSafe
public final class SkipBloatUnderThresholdPredicate implements Predicate<DbObject> {

private final long sizeThresholdInBytes;
private final double percentageThreshold;

private SkipBloatUnderThresholdPredicate(final long sizeThresholdInBytes, final double percentageThreshold) {
this.sizeThresholdInBytes = Validators.sizeNotNegative(sizeThresholdInBytes, "sizeThresholdInBytes");
this.percentageThreshold = Validators.validPercent(percentageThreshold, "percentageThreshold");
}

/**
* Tests whether the specified {@code DbObject} meets or exceeds the bloat thresholds.
* <p>
* If {@code dbObject} implements {@link BloatAware}, its bloat size and percentage are checked
* against the thresholds. If {@code dbObject} does not implement {@link BloatAware}, it passes the test by default.
* </p>
*
* @param dbObject the database object to test
* @return {@code true} if the {@code dbObject} meets or exceeds the thresholds or does not implement {@link BloatAware}; {@code false} if it is below the thresholds
*/
@Override
public boolean test(@Nonnull final DbObject dbObject) {
if (sizeThresholdInBytes == 0L && percentageThreshold == 0.0) {
return true;
}
if (!(dbObject instanceof BloatAware)) {
return true;
}
final BloatAware bloatAware = (BloatAware) dbObject;
return bloatAware.getBloatSizeInBytes() >= sizeThresholdInBytes &&
bloatAware.getBloatPercentage() >= percentageThreshold;
}

/**
* Creates a predicate to skip {@link BloatAware} objects with bloat below specified thresholds.
*
* @param sizeThresholdInBytes minimum bloat size in bytes required for a {@link BloatAware} object to pass the test; must be non-negative
* @param percentageThreshold minimum bloat percentage required for a {@link BloatAware} object to pass the test; must be a valid percentage (0-100)
* @return a {@link Predicate} that skips objects below the specified bloat thresholds
*/
@Nonnull
public static Predicate<DbObject> of(final long sizeThresholdInBytes, final double percentageThreshold) {
return new SkipBloatUnderThresholdPredicate(sizeThresholdInBytes, percentageThreshold);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2019-2024. Ivan Vakhrushev and others.
* https://github.com/mfvanek/pg-index-health
*
* This file is a part of "pg-index-health" - a Java library for
* analyzing and maintaining indexes health in PostgreSQL databases.
*
* Licensed under the Apache License 2.0
*/

package io.github.mfvanek.pg.model.predicates;

import io.github.mfvanek.pg.model.index.IndexWithBloat;
import io.github.mfvanek.pg.model.sequence.SequenceState;
import io.github.mfvanek.pg.model.table.Table;
import io.github.mfvanek.pg.model.table.TableWithBloat;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

class SkipBloatUnderThresholdPredicateTest {

@Test
void shouldThrowExceptionWhenInvalidDataPassed() {
assertThatThrownBy(() -> SkipBloatUnderThresholdPredicate.of(-1L, -1.0))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("sizeThresholdInBytes cannot be less than zero");

assertThatThrownBy(() -> SkipBloatUnderThresholdPredicate.of(0L, -1.0))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("percentageThreshold should be in the range from 0.0 to 100.0 inclusive");

assertThatThrownBy(() -> SkipBloatUnderThresholdPredicate.of(0L, 100.1))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("percentageThreshold should be in the range from 0.0 to 100.0 inclusive");
}

@Test
void shouldNotCastObjectsWhenThresholdIsZero() {
final TableWithBloat mockBloat = Mockito.mock(TableWithBloat.class);
assertThat(SkipBloatUnderThresholdPredicate.of(0L, 0.0))
.accepts(mockBloat);
Mockito.verify(mockBloat, Mockito.never()).getBloatPercentage();
Mockito.verify(mockBloat, Mockito.never()).getBloatSizeInBytes();
}

@Test
void shouldWork() {
assertThat(SkipBloatUnderThresholdPredicate.of(100L, 10.0))
.accepts(Table.of("t", 0L))
.accepts(SequenceState.of("s1", "int", 80.0))
.rejects(TableWithBloat.of(Table.of("t2", 1L), 1L, 10.0))
.rejects(TableWithBloat.of(Table.of("t2", 1L), 1L, 11.0))
.accepts(TableWithBloat.of(Table.of("t2", 100L), 100L, 10.0))
.accepts(TableWithBloat.of(Table.of("t2", 200L), 101L, 10.0))
.rejects(IndexWithBloat.of("t2", "i2", 1L, 1L, 10.0))
.rejects(IndexWithBloat.of("t2", "i2", 1L, 1L, 11.0))
.accepts(IndexWithBloat.of("t2", "i2", 100L, 100L, 10.0))
.accepts(IndexWithBloat.of("t2", "i2", 200L, 101L, 10.0));

assertThat(SkipBloatUnderThresholdPredicate.of(0L, 1.0))
.rejects(TableWithBloat.of(Table.of("t2", 1L), 0L, 0.1))
.accepts(TableWithBloat.of(Table.of("t2", 100L), 1L, 1.0));

assertThat(SkipBloatUnderThresholdPredicate.of(1L, 0.0))
.rejects(TableWithBloat.of(Table.of("t2", 1L), 0L, 0.1))
.accepts(TableWithBloat.of(Table.of("t2", 100L), 1L, 0.0));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
*
* @author Ivan Vakhrushev
* @since 0.6.0
* @deprecated This class has been replaced by {@link io.github.mfvanek.pg.model.predicates.SkipBloatUnderThresholdPredicate}
*/
@Deprecated(since = "0.13.3", forRemoval = true)
public class FilterIndexesByBloatPredicate extends AbstractFilterByBloat implements Predicate<IndexBloatAware> {

private FilterIndexesByBloatPredicate(final long sizeThresholdInBytes, final double percentageThreshold) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
*
* @author Ivan Vakhrushev
* @since 0.6.0
* @deprecated This class has been replaced by {@link io.github.mfvanek.pg.model.predicates.SkipBloatUnderThresholdPredicate}
*/
@Deprecated(since = "0.13.3", forRemoval = true)
public class FilterTablesByBloatPredicate extends AbstractFilterByBloat implements Predicate<TableBloatAware> {

private FilterTablesByBloatPredicate(final long sizeThresholdInBytes, final double percentageThreshold) {
Expand Down

0 comments on commit a18edeb

Please sign in to comment.