From 8029a92b96265651bd40af3d932df6b5f4b34b08 Mon Sep 17 00:00:00 2001 From: Ivan Vakhrushev Date: Wed, 13 Nov 2024 22:40:22 +0400 Subject: [PATCH] Add SkipSmallTablesPredicate (#492) * Add SkipSmallTablesPredicate * Refactor tests * Rename method --- ...blesWithMissingIndexesCheckOnHostTest.java | 5 ++ ...blesWithoutDescriptionCheckOnHostTest.java | 12 +++- .../health/logger/AbstractHealthLogger.java | 23 +++--- .../github/mfvanek/pg/model/index/Index.java | 6 ++ .../predicates/AbstractFilterBySize.java | 48 +++++++++++++ .../SkipBySequenceNamePredicate.java | 2 +- .../SkipIndexesByNamePredicate.java | 2 +- .../predicates/SkipSmallTablesPredicate.java | 71 +++++++++++++++++++ .../predicates/SkipTablesByNamePredicate.java | 2 +- .../SkipBySequenceNamePredicateTest.java | 6 +- .../SkipIndexesByNamePredicateTest.java | 6 +- .../SkipSmallTablesPredicateTest.java | 48 +++++++++++++ .../SkipTablesByNamePredicateTest.java | 4 +- .../FilterTablesBySizePredicate.java | 2 + ...sWithMissingIndexesCheckOnClusterTest.java | 13 +--- ...sWithoutDescriptionCheckOnClusterTest.java | 7 +- 16 files changed, 218 insertions(+), 39 deletions(-) create mode 100644 pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/AbstractFilterBySize.java create mode 100644 pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipSmallTablesPredicate.java create mode 100644 pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipSmallTablesPredicateTest.java diff --git a/pg-index-health-core/src/test/java/io/github/mfvanek/pg/checks/host/TablesWithMissingIndexesCheckOnHostTest.java b/pg-index-health-core/src/test/java/io/github/mfvanek/pg/checks/host/TablesWithMissingIndexesCheckOnHostTest.java index 3fa5de15..78a77972 100644 --- a/pg-index-health-core/src/test/java/io/github/mfvanek/pg/checks/host/TablesWithMissingIndexesCheckOnHostTest.java +++ b/pg-index-health-core/src/test/java/io/github/mfvanek/pg/checks/host/TablesWithMissingIndexesCheckOnHostTest.java @@ -13,6 +13,7 @@ import io.github.mfvanek.pg.common.maintenance.DatabaseCheckOnHost; import io.github.mfvanek.pg.common.maintenance.Diagnostic; import io.github.mfvanek.pg.model.PgContext; +import io.github.mfvanek.pg.model.predicates.SkipSmallTablesPredicate; import io.github.mfvanek.pg.model.predicates.SkipTablesByNamePredicate; import io.github.mfvanek.pg.model.table.TableWithMissingIndex; import io.github.mfvanek.pg.support.StatisticsAwareTestBase; @@ -52,6 +53,10 @@ void onDatabaseWithThem(final String schemaName) { assertThat(check) .executing(ctx, SkipTablesByNamePredicate.ofName(ctx, "accounts")) .isEmpty(); + + assertThat(check) + .executing(ctx, SkipSmallTablesPredicate.of(1_000_000L)) + .isEmpty(); }); } } diff --git a/pg-index-health-core/src/test/java/io/github/mfvanek/pg/checks/host/TablesWithoutDescriptionCheckOnHostTest.java b/pg-index-health-core/src/test/java/io/github/mfvanek/pg/checks/host/TablesWithoutDescriptionCheckOnHostTest.java index f0deb00c..52e38b74 100644 --- a/pg-index-health-core/src/test/java/io/github/mfvanek/pg/checks/host/TablesWithoutDescriptionCheckOnHostTest.java +++ b/pg-index-health-core/src/test/java/io/github/mfvanek/pg/checks/host/TablesWithoutDescriptionCheckOnHostTest.java @@ -13,6 +13,7 @@ import io.github.mfvanek.pg.common.maintenance.DatabaseCheckOnHost; import io.github.mfvanek.pg.common.maintenance.Diagnostic; import io.github.mfvanek.pg.model.PgContext; +import io.github.mfvanek.pg.model.predicates.SkipSmallTablesPredicate; import io.github.mfvanek.pg.model.predicates.SkipTablesByNamePredicate; import io.github.mfvanek.pg.model.table.Table; import io.github.mfvanek.pg.support.DatabaseAwareTestBase; @@ -58,12 +59,19 @@ void onDatabaseWithThem(final String schemaName) { @ParameterizedTest @ValueSource(strings = {PgContext.DEFAULT_SCHEMA_NAME, "custom"}) void shouldNotTakingIntoAccountBlankComments(final String schemaName) { - executeTestOnDatabase(schemaName, dbp -> dbp.withReferences().withBlankCommentOnTables(), ctx -> + executeTestOnDatabase(schemaName, dbp -> dbp.withReferences().withBlankCommentOnTables(), ctx -> { assertThat(check) .executing(ctx) .hasSize(2) .containsExactly( Table.of(ctx.enrichWithSchema("accounts"), 0L), - Table.of(ctx.enrichWithSchema("clients"), 0L))); + Table.of(ctx.enrichWithSchema("clients"), 0L)); + + assertThat(check) + .executing(ctx, SkipSmallTablesPredicate.of(1_234L)) + .hasSize(1) + .containsExactly(Table.of(ctx.enrichWithSchema("clients"), 0L)) + .allMatch(t -> t.getTableSizeInBytes() > 1_234L); + }); } } diff --git a/pg-index-health-logger/src/main/java/io/github/mfvanek/pg/common/health/logger/AbstractHealthLogger.java b/pg-index-health-logger/src/main/java/io/github/mfvanek/pg/common/health/logger/AbstractHealthLogger.java index 97b37297..3b5d496a 100644 --- a/pg-index-health-logger/src/main/java/io/github/mfvanek/pg/common/health/logger/AbstractHealthLogger.java +++ b/pg-index-health-logger/src/main/java/io/github/mfvanek/pg/common/health/logger/AbstractHealthLogger.java @@ -14,9 +14,6 @@ import io.github.mfvanek.pg.checks.predicates.FilterIndexesByBloatPredicate; import io.github.mfvanek.pg.checks.predicates.FilterIndexesByNamePredicate; import io.github.mfvanek.pg.checks.predicates.FilterIndexesBySizePredicate; -import io.github.mfvanek.pg.checks.predicates.FilterTablesByBloatPredicate; -import io.github.mfvanek.pg.checks.predicates.FilterTablesByNamePredicate; -import io.github.mfvanek.pg.checks.predicates.FilterTablesBySizePredicate; import io.github.mfvanek.pg.common.maintenance.DatabaseCheckOnCluster; import io.github.mfvanek.pg.common.maintenance.DatabaseChecks; import io.github.mfvanek.pg.common.maintenance.Diagnostic; @@ -38,6 +35,10 @@ import io.github.mfvanek.pg.model.index.IndexWithNulls; import io.github.mfvanek.pg.model.index.UnusedIndex; import io.github.mfvanek.pg.model.object.AnyObject; +import io.github.mfvanek.pg.model.predicates.SkipBloatUnderThresholdPredicate; +import io.github.mfvanek.pg.model.predicates.SkipIndexesByNamePredicate; +import io.github.mfvanek.pg.model.predicates.SkipSmallTablesPredicate; +import io.github.mfvanek.pg.model.predicates.SkipTablesByNamePredicate; import io.github.mfvanek.pg.model.sequence.SequenceState; import io.github.mfvanek.pg.model.table.Table; import io.github.mfvanek.pg.model.table.TableWithBloat; @@ -150,21 +151,21 @@ private String logUnusedIndexes(@Nonnull final Exclusions exclusions) { @Nonnull private String logTablesWithMissingIndexes(@Nonnull final Exclusions exclusions) { return logCheckResult(databaseChecksHolder.get().getCheck(Diagnostic.TABLES_WITH_MISSING_INDEXES, TableWithMissingIndex.class), - FilterTablesBySizePredicate.of(exclusions.getTableSizeThresholdInBytes()) - .and(FilterTablesByNamePredicate.of(exclusions.getTablesWithMissingIndexesExclusions())), SimpleLoggingKey.TABLES_WITH_MISSING_INDEXES); + SkipSmallTablesPredicate.of(exclusions.getTableSizeThresholdInBytes()) + .and(SkipTablesByNamePredicate.of(pgContextHolder.get(), exclusions.getTablesWithMissingIndexesExclusions())), SimpleLoggingKey.TABLES_WITH_MISSING_INDEXES); } @Nonnull private String logTablesWithoutPrimaryKey(@Nonnull final Exclusions exclusions) { return logCheckResult(databaseChecksHolder.get().getCheck(Diagnostic.TABLES_WITHOUT_PRIMARY_KEY, Table.class), - FilterTablesBySizePredicate.of(exclusions.getTableSizeThresholdInBytes()) - .and(FilterTablesByNamePredicate.of(exclusions.getTablesWithoutPrimaryKeyExclusions())), SimpleLoggingKey.TABLES_WITHOUT_PRIMARY_KEY); + SkipSmallTablesPredicate.of(exclusions.getTableSizeThresholdInBytes()) + .and(SkipTablesByNamePredicate.of(pgContextHolder.get(), exclusions.getTablesWithoutPrimaryKeyExclusions())), SimpleLoggingKey.TABLES_WITHOUT_PRIMARY_KEY); } @Nonnull private String logIndexesWithNullValues(@Nonnull final Exclusions exclusions) { return logCheckResult(databaseChecksHolder.get().getCheck(Diagnostic.INDEXES_WITH_NULL_VALUES, IndexWithNulls.class), - FilterIndexesByNamePredicate.of(exclusions.getIndexesWithNullValuesExclusions()), SimpleLoggingKey.INDEXES_WITH_NULL_VALUES); + SkipIndexesByNamePredicate.of(pgContextHolder.get(), exclusions.getIndexesWithNullValuesExclusions()), SimpleLoggingKey.INDEXES_WITH_NULL_VALUES); } @Nonnull @@ -177,13 +178,13 @@ private String logIndexesBloat(@Nonnull final Exclusions exclusions) { @Nonnull private String logTablesBloat(@Nonnull final Exclusions exclusions) { return logCheckResult(databaseChecksHolder.get().getCheck(Diagnostic.BLOATED_TABLES, TableWithBloat.class), - FilterTablesByBloatPredicate.of(exclusions.getTableBloatSizeThresholdInBytes(), exclusions.getTableBloatPercentageThreshold()) - .and(FilterTablesBySizePredicate.of(exclusions.getTableSizeThresholdInBytes())), SimpleLoggingKey.BLOATED_TABLES); + SkipBloatUnderThresholdPredicate.of(exclusions.getTableBloatSizeThresholdInBytes(), exclusions.getTableBloatPercentageThreshold()) + .and(SkipSmallTablesPredicate.of(exclusions.getTableSizeThresholdInBytes())), SimpleLoggingKey.BLOATED_TABLES); } private String logBtreeIndexesOnArrayColumns(@Nonnull final Exclusions exclusions) { return logCheckResult(databaseChecksHolder.get().getCheck(Diagnostic.BTREE_INDEXES_ON_ARRAY_COLUMNS, Index.class), - FilterIndexesByNamePredicate.of(exclusions.getBtreeIndexesOnArrayColumnsExclusions()), SimpleLoggingKey.BTREE_INDEXES_ON_ARRAY_COLUMNS); + SkipIndexesByNamePredicate.of(pgContextHolder.get(), exclusions.getBtreeIndexesOnArrayColumnsExclusions()), SimpleLoggingKey.BTREE_INDEXES_ON_ARRAY_COLUMNS); } @Nonnull diff --git a/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/index/Index.java b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/index/Index.java index 20f61550..b93c195a 100644 --- a/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/index/Index.java +++ b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/index/Index.java @@ -32,6 +32,12 @@ public class Index implements DbObject, TableNameAware, IndexNameAware, Comparab private final String tableName; private final String indexName; + /** + * Constructs an {@code Index} with the specified table and index names. + * + * @param tableName the name of the table associated with this index; must be non-blank. + * @param indexName the name of this index; must be non-blank. + */ @SuppressWarnings("WeakerAccess") protected Index(@Nonnull final String tableName, @Nonnull final String indexName) { this.tableName = Validators.tableNameNotBlank(tableName); diff --git a/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/AbstractFilterBySize.java b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/AbstractFilterBySize.java new file mode 100644 index 00000000..b6078b32 --- /dev/null +++ b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/AbstractFilterBySize.java @@ -0,0 +1,48 @@ +/* + * 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.DbObject; +import io.github.mfvanek.pg.model.validation.Validators; + +import java.util.function.Predicate; + +/** + * An abstract base class for filtering {@link DbObject} instances based on a size threshold. + * This class implements {@link Predicate} and serves as a foundation for classes that + * filter database objects according to their size in bytes. + * + *

Subclasses must implement the {@code test} method to specify the filtering logic.

+ * + * @author Ivan Vakhrushev + * @see DbObject + * @see Predicate + * @since 0.13.3 + */ +abstract class AbstractFilterBySize implements Predicate { + + /** + * The size threshold in bytes for filtering {@link DbObject} instances. + * Subclasses may use this threshold to include or exclude objects based on their size. + */ + protected final long thresholdInBytes; + + /** + * Constructs an {@code AbstractFilterBySize} with the specified size threshold. + * + * @param thresholdInBytes the minimum size in bytes for an object to pass the filter; + * must be non-negative. + * @throws IllegalArgumentException if {@code thresholdInBytes} is negative. + */ + protected AbstractFilterBySize(final long thresholdInBytes) { + this.thresholdInBytes = Validators.sizeNotNegative(thresholdInBytes, "thresholdInBytes"); + } +} diff --git a/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipBySequenceNamePredicate.java b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipBySequenceNamePredicate.java index b722498c..4dbac0d5 100644 --- a/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipBySequenceNamePredicate.java +++ b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipBySequenceNamePredicate.java @@ -77,7 +77,7 @@ public static Predicate ofName(@Nonnull final String rawSequenceNameTo * @param rawSequenceNamesToSkip the collection of raw sequence names to skip; must be non-null * @return a {@code SkipBySequenceNamePredicate} instance for the specified sequence names */ - public static Predicate of(@Nonnull final Collection rawSequenceNamesToSkip) { + public static Predicate ofPublic(@Nonnull final Collection rawSequenceNamesToSkip) { return new SkipBySequenceNamePredicate(PgContext.ofPublic(), rawSequenceNamesToSkip); } diff --git a/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipIndexesByNamePredicate.java b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipIndexesByNamePredicate.java index 349258bf..c56f241c 100644 --- a/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipIndexesByNamePredicate.java +++ b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipIndexesByNamePredicate.java @@ -83,7 +83,7 @@ public static Predicate ofName(@Nonnull final String rawIndexNameToSki * @param rawIndexNamesToSkip a collection of raw index names to skip; must be non-null * @return a {@link Predicate} to skip the specified index names */ - public static Predicate of(@Nonnull final Collection rawIndexNamesToSkip) { + public static Predicate ofPublic(@Nonnull final Collection rawIndexNamesToSkip) { return new SkipIndexesByNamePredicate(PgContext.ofPublic(), rawIndexNamesToSkip); } diff --git a/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipSmallTablesPredicate.java b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipSmallTablesPredicate.java new file mode 100644 index 00000000..15a9750e --- /dev/null +++ b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipSmallTablesPredicate.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.DbObject; +import io.github.mfvanek.pg.model.table.TableSizeAware; + +import java.util.function.Predicate; +import javax.annotation.Nonnull; +import javax.annotation.concurrent.Immutable; +import javax.annotation.concurrent.ThreadSafe; + +/** + * A predicate that filters out small tables based on a specified size threshold. + * This class extends {@link AbstractFilterBySize} and evaluates {@link TableSizeAware} + * instances to determine if they meet or exceed the specified minimum table size. + * + * @author Ivan Vakhrushev + * @see TableSizeAware + * @see DbObject + * @see AbstractFilterBySize + * @since 0.13.3 + */ +@Immutable +@ThreadSafe +public final class SkipSmallTablesPredicate extends AbstractFilterBySize { + + private SkipSmallTablesPredicate(final long thresholdInBytes) { + super(thresholdInBytes); + } + + /** + * Evaluates whether the given {@link DbObject} should pass the filter based on its size. + * If the object is not an instance of {@link TableSizeAware}, or if the threshold is zero, + * it automatically passes the filter. Otherwise, the object's size is compared to the threshold. + * + * @param dbObject the {@code DbObject} to be evaluated; must not be null. + * @return {@code true} if the table size is greater than or equal to the threshold, or if the object is not {@code TableSizeAware}; {@code false} otherwise. + */ + @Override + public boolean test(@Nonnull final DbObject dbObject) { + if (thresholdInBytes == 0L) { + return true; + } + if (!(dbObject instanceof TableSizeAware)) { + return true; + } + final TableSizeAware tableSizeAware = (TableSizeAware) dbObject; + return tableSizeAware.getTableSizeInBytes() >= thresholdInBytes; + } + + /** + * Creates a {@code SkipSmallTablesPredicate} with the specified size threshold. + * + * @param thresholdInBytes the minimum table size in bytes required for a table to pass the filter; + * must be non-negative. + * @return a predicate that filters out tables smaller than the specified size threshold. + */ + @Nonnull + public static Predicate of(final long thresholdInBytes) { + return new SkipSmallTablesPredicate(thresholdInBytes); + } +} diff --git a/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipTablesByNamePredicate.java b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipTablesByNamePredicate.java index d6affc96..2aed17dd 100644 --- a/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipTablesByNamePredicate.java +++ b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipTablesByNamePredicate.java @@ -62,7 +62,7 @@ public static Predicate ofName(@Nonnull final String rawTableNameToSki * @return a predicate that skips the specified tables in the "public" schema * @throws NullPointerException if {@code rawTableNamesToSkip} is null */ - public static Predicate of(@Nonnull final Collection rawTableNamesToSkip) { + public static Predicate ofPublic(@Nonnull final Collection rawTableNamesToSkip) { return new SkipTablesByNamePredicate(PgContext.ofPublic(), rawTableNamesToSkip); } diff --git a/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipBySequenceNamePredicateTest.java b/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipBySequenceNamePredicateTest.java index e6c5eae6..02585e44 100644 --- a/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipBySequenceNamePredicateTest.java +++ b/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipBySequenceNamePredicateTest.java @@ -43,7 +43,7 @@ void shouldThrowExceptionWhenInvalidDataPassed() { .isInstanceOf(IllegalArgumentException.class) .hasMessage("rawSequenceNameToSkip cannot be blank"); - assertThatThrownBy(() -> SkipBySequenceNamePredicate.of(null)) + assertThatThrownBy(() -> SkipBySequenceNamePredicate.ofPublic(null)) .isInstanceOf(NullPointerException.class) .hasMessage("rawNamesToSkip cannot be null"); @@ -72,7 +72,7 @@ void shouldThrowExceptionWhenInvalidDataPassed() { @Test void shouldNotCastObjectsWhenExclusionsIsEmpty() { final SequenceState mockSequence = Mockito.mock(SequenceState.class); - assertThat(SkipBySequenceNamePredicate.of(List.of())) + assertThat(SkipBySequenceNamePredicate.ofPublic(List.of())) .accepts(mockSequence); Mockito.verify(mockSequence, Mockito.never()).getSequenceName(); } @@ -95,7 +95,7 @@ void shouldWorkForSingleSequence() { @Test void shouldWorkForMultipleSequences() { - assertThat(SkipBySequenceNamePredicate.of(Set.of("s1", "S2"))) + assertThat(SkipBySequenceNamePredicate.ofPublic(Set.of("s1", "S2"))) .accepts(Table.of("t", 0L)) .accepts(SequenceState.of("s11", "int", 80.0)) .rejects(SequenceState.of("s1", "int", 80.0)) diff --git a/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipIndexesByNamePredicateTest.java b/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipIndexesByNamePredicateTest.java index d6b1ca32..9e19e864 100644 --- a/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipIndexesByNamePredicateTest.java +++ b/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipIndexesByNamePredicateTest.java @@ -44,7 +44,7 @@ void shouldThrowExceptionWhenInvalidDataPassed() { .isInstanceOf(IllegalArgumentException.class) .hasMessage("rawIndexNameToSkip cannot be blank"); - assertThatThrownBy(() -> SkipIndexesByNamePredicate.of(null)) + assertThatThrownBy(() -> SkipIndexesByNamePredicate.ofPublic(null)) .isInstanceOf(NullPointerException.class) .hasMessage("rawNamesToSkip cannot be null"); @@ -73,7 +73,7 @@ void shouldThrowExceptionWhenInvalidDataPassed() { @Test void shouldNotCastObjectsWhenExclusionsIsEmpty() { final Index mockIndex = Mockito.mock(Index.class); - assertThat(SkipIndexesByNamePredicate.of(List.of())) + assertThat(SkipIndexesByNamePredicate.ofPublic(List.of())) .accepts(mockIndex); Mockito.verify(mockIndex, Mockito.never()).getIndexName(); } @@ -96,7 +96,7 @@ void shouldWorkForSingleIndex() { @Test void shouldWorkForMultipleIndexes() { - assertThat(SkipIndexesByNamePredicate.of(Set.of("i1", "I2"))) + assertThat(SkipIndexesByNamePredicate.ofPublic(Set.of("i1", "I2"))) .accepts(Table.of("t", 0L)) .accepts(SequenceState.of("s11", "int", 80.0)) .accepts(ColumnWithSerialType.ofSerial(Column.ofNullable("t", "c"), "s1")) diff --git a/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipSmallTablesPredicateTest.java b/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipSmallTablesPredicateTest.java new file mode 100644 index 00000000..ae179532 --- /dev/null +++ b/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipSmallTablesPredicateTest.java @@ -0,0 +1,48 @@ +/* + * 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.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 SkipSmallTablesPredicateTest { + + @Test + void shouldThrowExceptionWhenInvalidDataPassed() { + assertThatThrownBy(() -> SkipSmallTablesPredicate.of(-1L)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("thresholdInBytes cannot be less than zero"); + } + + @Test + void shouldNotCastObjectsWhenThresholdIsZero() { + final Table mockTable = Mockito.mock(Table.class); + assertThat(SkipSmallTablesPredicate.of(0L)) + .accepts(mockTable); + Mockito.verify(mockTable, Mockito.never()).getTableSizeInBytes(); + } + + @Test + void shouldWork() { + assertThat(SkipSmallTablesPredicate.of(10L)) + .accepts(Table.of("t", 10L)) + .accepts(Table.of("t", 11L)) + .accepts(SequenceState.of("s1", "int", 80.0)) + .rejects(TableWithBloat.of(Table.of("t2", 1L), 1L, 10.0)) + .rejects(Table.of("t2", 1L)); + } +} diff --git a/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipTablesByNamePredicateTest.java b/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipTablesByNamePredicateTest.java index ed83ea00..a8425063 100644 --- a/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipTablesByNamePredicateTest.java +++ b/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipTablesByNamePredicateTest.java @@ -40,7 +40,7 @@ void shouldThrowExceptionWhenInvalidDataPassed() { .isInstanceOf(IllegalArgumentException.class) .hasMessage("rawTableNameToSkip cannot be blank"); - assertThatThrownBy(() -> SkipTablesByNamePredicate.of(null)) + assertThatThrownBy(() -> SkipTablesByNamePredicate.ofPublic(null)) .isInstanceOf(NullPointerException.class) .hasMessage("rawNamesToSkip cannot be null"); @@ -82,7 +82,7 @@ void shouldWorkForSingleTable() { @Test void shouldWorkForMultipleTables() { - assertThat(SkipTablesByNamePredicate.of(Set.of("t", "T2"))) + assertThat(SkipTablesByNamePredicate.ofPublic(Set.of("t", "T2"))) .accepts(Index.of("t1", "i1")) .rejects(Index.of("t", "i")) .rejects(Index.of("T", "I")) diff --git a/pg-index-health/src/main/java/io/github/mfvanek/pg/checks/predicates/FilterTablesBySizePredicate.java b/pg-index-health/src/main/java/io/github/mfvanek/pg/checks/predicates/FilterTablesBySizePredicate.java index f4eb04bd..d8bfbfd3 100644 --- a/pg-index-health/src/main/java/io/github/mfvanek/pg/checks/predicates/FilterTablesBySizePredicate.java +++ b/pg-index-health/src/main/java/io/github/mfvanek/pg/checks/predicates/FilterTablesBySizePredicate.java @@ -21,7 +21,9 @@ * @author Ivan Vakhrushev * @see TableSizeAware * @since 0.6.0 + * @deprecated This class has been replaced by {@link io.github.mfvanek.pg.model.predicates.SkipSmallTablesPredicate} */ +@Deprecated(since = "0.13.3", forRemoval = true) public class FilterTablesBySizePredicate extends AbstractFilterBySize implements Predicate { private FilterTablesBySizePredicate(final long thresholdInBytes) { diff --git a/pg-index-health/src/test/java/io/github/mfvanek/pg/checks/cluster/TablesWithMissingIndexesCheckOnClusterTest.java b/pg-index-health/src/test/java/io/github/mfvanek/pg/checks/cluster/TablesWithMissingIndexesCheckOnClusterTest.java index 669c40a5..0bbef44d 100644 --- a/pg-index-health/src/test/java/io/github/mfvanek/pg/checks/cluster/TablesWithMissingIndexesCheckOnClusterTest.java +++ b/pg-index-health/src/test/java/io/github/mfvanek/pg/checks/cluster/TablesWithMissingIndexesCheckOnClusterTest.java @@ -10,10 +10,10 @@ package io.github.mfvanek.pg.checks.cluster; -import io.github.mfvanek.pg.checks.predicates.FilterTablesBySizePredicate; import io.github.mfvanek.pg.common.maintenance.DatabaseCheckOnCluster; import io.github.mfvanek.pg.common.maintenance.Diagnostic; import io.github.mfvanek.pg.model.PgContext; +import io.github.mfvanek.pg.model.predicates.SkipSmallTablesPredicate; import io.github.mfvanek.pg.model.predicates.SkipTablesByNamePredicate; import io.github.mfvanek.pg.model.table.TableWithMissingIndex; import io.github.mfvanek.pg.support.StatisticsAwareTestBase; @@ -57,16 +57,7 @@ void onDatabaseWithThem(final String schemaName) { .isEmpty(); assertThat(check) - .executing(ctx, FilterTablesBySizePredicate.of(1L)) - .hasSize(1) - .containsExactly( - TableWithMissingIndex.of(ctx.enrichWithSchema("accounts"), 0L, 0L, 0L)) - .allMatch(t -> t.getSeqScans() >= AMOUNT_OF_TRIES) - .allMatch(t -> t.getIndexScans() == 0) - .allMatch(t -> t.getTableSizeInBytes() > 1L); - - assertThat(check) - .executing(ctx, FilterTablesBySizePredicate.of(1_000_000L)) + .executing(ctx, SkipSmallTablesPredicate.of(1_000_000L)) .isEmpty(); }); } diff --git a/pg-index-health/src/test/java/io/github/mfvanek/pg/checks/cluster/TablesWithoutDescriptionCheckOnClusterTest.java b/pg-index-health/src/test/java/io/github/mfvanek/pg/checks/cluster/TablesWithoutDescriptionCheckOnClusterTest.java index eca03ef1..1fc46586 100644 --- a/pg-index-health/src/test/java/io/github/mfvanek/pg/checks/cluster/TablesWithoutDescriptionCheckOnClusterTest.java +++ b/pg-index-health/src/test/java/io/github/mfvanek/pg/checks/cluster/TablesWithoutDescriptionCheckOnClusterTest.java @@ -10,10 +10,10 @@ package io.github.mfvanek.pg.checks.cluster; -import io.github.mfvanek.pg.checks.predicates.FilterTablesBySizePredicate; import io.github.mfvanek.pg.common.maintenance.DatabaseCheckOnCluster; import io.github.mfvanek.pg.common.maintenance.Diagnostic; import io.github.mfvanek.pg.model.PgContext; +import io.github.mfvanek.pg.model.predicates.SkipSmallTablesPredicate; import io.github.mfvanek.pg.model.predicates.SkipTablesByNamePredicate; import io.github.mfvanek.pg.model.table.Table; import io.github.mfvanek.pg.support.DatabaseAwareTestBase; @@ -69,10 +69,9 @@ void shouldNotTakingIntoAccountBlankComments(final String schemaName) { Table.of(ctx.enrichWithSchema("clients"), 0L)); assertThat(check) - .executing(ctx, FilterTablesBySizePredicate.of(1_234L)) + .executing(ctx, SkipSmallTablesPredicate.of(1_234L)) .hasSize(1) - .containsExactly( - Table.of(ctx.enrichWithSchema("clients"), 0L)) + .containsExactly(Table.of(ctx.enrichWithSchema("clients"), 0L)) .allMatch(t -> t.getTableSizeInBytes() > 1_234L); }); }