diff --git a/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/index/IndexWithSize.java b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/index/IndexWithSize.java index 5ed2a054..b13ec8b9 100644 --- a/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/index/IndexWithSize.java +++ b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/index/IndexWithSize.java @@ -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, diff --git a/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipBloatUnderThresholdPredicate.java b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipBloatUnderThresholdPredicate.java new file mode 100644 index 00000000..0f36a43c --- /dev/null +++ b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipBloatUnderThresholdPredicate.java @@ -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 { + + 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. + *

+ * 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. + *

+ * + * @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 of(final long sizeThresholdInBytes, final double percentageThreshold) { + return new SkipBloatUnderThresholdPredicate(sizeThresholdInBytes, percentageThreshold); + } +} diff --git a/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipBloatUnderThresholdPredicateTest.java b/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipBloatUnderThresholdPredicateTest.java new file mode 100644 index 00000000..248d2174 --- /dev/null +++ b/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipBloatUnderThresholdPredicateTest.java @@ -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)); + } +} diff --git a/pg-index-health/src/main/java/io/github/mfvanek/pg/checks/predicates/FilterIndexesByBloatPredicate.java b/pg-index-health/src/main/java/io/github/mfvanek/pg/checks/predicates/FilterIndexesByBloatPredicate.java index 4316f0e5..fe12099e 100644 --- a/pg-index-health/src/main/java/io/github/mfvanek/pg/checks/predicates/FilterIndexesByBloatPredicate.java +++ b/pg-index-health/src/main/java/io/github/mfvanek/pg/checks/predicates/FilterIndexesByBloatPredicate.java @@ -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 { private FilterIndexesByBloatPredicate(final long sizeThresholdInBytes, final double percentageThreshold) { diff --git a/pg-index-health/src/main/java/io/github/mfvanek/pg/checks/predicates/FilterTablesByBloatPredicate.java b/pg-index-health/src/main/java/io/github/mfvanek/pg/checks/predicates/FilterTablesByBloatPredicate.java index a4c91c27..b53c96cf 100644 --- a/pg-index-health/src/main/java/io/github/mfvanek/pg/checks/predicates/FilterTablesByBloatPredicate.java +++ b/pg-index-health/src/main/java/io/github/mfvanek/pg/checks/predicates/FilterTablesByBloatPredicate.java @@ -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 { private FilterTablesByBloatPredicate(final long sizeThresholdInBytes, final double percentageThreshold) {