This is an automated email from the ASF dual-hosted git repository.

korlov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 539756abd57 IGNITE-26486 Catalog. Add DATA_STALENESS configuration to 
table structures (#6695)
539756abd57 is described below

commit 539756abd57a4ce152f21ceb653e2c0e8d530af3
Author: korlov42 <[email protected]>
AuthorDate: Thu Oct 9 08:58:38 2025 +0300

    IGNITE-26486 Catalog. Add DATA_STALENESS configuration to table structures 
(#6695)
---
 .../commands/AlterTableSetPropertyCommand.java     | 170 +++++++++++++++++++++
 .../AlterTableSetPropertyCommandBuilder.java       |  32 ++++
 .../internal/catalog/commands/CatalogUtils.java    |   3 +
 .../catalog/commands/CreateTableCommand.java       |  60 ++++++--
 .../commands/CreateTableCommandBuilder.java        |   6 +
 .../descriptors/CatalogTableDescriptor.java        |  44 +++++-
 .../CatalogTableDescriptorSerializers.java         |  11 +-
 .../descriptors/CatalogTableProperties.java        |  49 ++++++
 .../AlterTablePropertiesEventParameters.java       |  62 ++++++++
 .../catalog/storage/AlterTablePropertiesEntry.java | 103 +++++++++++++
 .../AlterTablePropertiesEntrySerializers.java      |  63 ++++++++
 .../serialization/CatalogSerializationUtils.java   |  36 +++++
 .../serialization/MarshallableEntryType.java       |   4 +-
 .../ignite/internal/catalog/CatalogTableTest.java  | 159 +++++++++++++++++++
 ...AlterTableSetPropertyCommandValidationTest.java | 156 +++++++++++++++++++
 .../commands/CreateTableCommandValidationTest.java |  37 ++++-
 .../storage/CatalogEntrySerializationTest.java     |  31 +++-
 ...logSerializationCompatibilityV2ReadsV1Test.java |  15 ++
 .../serialization_v1/AlterTableProperties_1.bin    | Bin 0 -> 80 bytes
 .../serialization_v2/NewSchemaEntry_2.bin          | Bin 165269 -> 117341 bytes
 .../resources/serialization_v2/NewTableEntry_2.bin | Bin 51763 -> 35787 bytes
 .../SnapshotEntryNoDefaultZone_2.bin               | Bin 165394 -> 117466 bytes
 .../resources/serialization_v2/SnapshotEntry_2.bin | Bin 165397 -> 117469 bytes
 .../ignite/internal/index/IndexManagerTest.java    |   4 +-
 .../ItPartitionModificationCounterMetricsTest.java |   2 +-
 .../apache/ignite/internal/table/TableImpl.java    |  24 ++-
 .../ignite/internal/table/TableViewInternal.java   |  20 +++
 .../distributed/PartitionModificationCounter.java  |  42 ++---
 .../PartitionModificationCounterFactory.java       |  25 ++-
 .../internal/table/distributed/TableManager.java   |  34 ++++-
 .../TableStatsStalenessConfiguration.java          |  88 +++++++++++
 .../PartitionModificationCounterTest.java          |  50 +++---
 .../ignite/internal/table/TableTestUtils.java      |   8 +-
 33 files changed, 1261 insertions(+), 77 deletions(-)

diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableSetPropertyCommand.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableSetPropertyCommand.java
new file mode 100644
index 00000000000..3e472993b93
--- /dev/null
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableSetPropertyCommand.java
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.catalog.commands;
+
+import static org.apache.ignite.internal.catalog.commands.CatalogUtils.schema;
+import static org.apache.ignite.internal.catalog.commands.CatalogUtils.table;
+
+import java.util.List;
+import org.apache.ignite.internal.catalog.Catalog;
+import org.apache.ignite.internal.catalog.CatalogCommand;
+import org.apache.ignite.internal.catalog.CatalogValidationException;
+import org.apache.ignite.internal.catalog.UpdateContext;
+import org.apache.ignite.internal.catalog.descriptors.CatalogSchemaDescriptor;
+import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
+import org.apache.ignite.internal.catalog.storage.AlterTablePropertiesEntry;
+import org.apache.ignite.internal.catalog.storage.UpdateEntry;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A command that changes table's properties.
+ */
+public class AlterTableSetPropertyCommand extends AbstractTableCommand {
+    /** Returns builder to create a command to add new columns to the table. */
+    public static AlterTableSetPropertyCommandBuilder builder() {
+        return new Builder();
+    }
+
+    private final @Nullable Double staleRowsFraction;
+    private final @Nullable Long minStaleRowsCount;
+
+    /**
+     * Constructs the object.
+     *
+     * @param tableName Name of the table to change properties. Should not be 
null or blank.
+     * @param schemaName Name of the schema the table of interest belongs to. 
Should not be null or blank.
+     * @param ifTableExists Flag indicating whether the {@code IF EXISTS} was 
specified.
+     * @param staleRowsFraction A fraction of a partition to be modified 
before the data is considered to be "stale". Should be in
+     *         range [0, 1].
+     * @param minStaleRowsCount Minimal number of rows in partition to be 
modified before the data is considered to be "stale".
+     *         Should be non-negative.
+     * @throws CatalogValidationException if any of restrictions above is 
violated.
+     */
+    private AlterTableSetPropertyCommand(
+            String tableName,
+            String schemaName,
+            boolean ifTableExists,
+            @Nullable Double staleRowsFraction,
+            @Nullable Long minStaleRowsCount
+    ) throws CatalogValidationException {
+        super(schemaName, tableName, ifTableExists, true);
+
+        this.staleRowsFraction = staleRowsFraction;
+        this.minStaleRowsCount = minStaleRowsCount;
+
+        validate();
+    }
+
+    @Override
+    public List<UpdateEntry> get(UpdateContext updateContext) {
+        Catalog catalog = updateContext.catalog();
+        CatalogSchemaDescriptor schema = schema(catalog, schemaName, 
!ifTableExists);
+        if (schema == null) {
+            return List.of();
+        }
+
+        CatalogTableDescriptor table = table(schema, tableName, 
!ifTableExists);
+        if (table == null) {
+            return List.of();
+        }
+
+        if (staleRowsFraction == null
+                && minStaleRowsCount == null
+        ) {
+            return List.of();
+        }
+
+        return List.of(
+                new AlterTablePropertiesEntry(
+                        table.id(),
+                        staleRowsFraction,
+                        minStaleRowsCount
+                )
+        );
+    }
+
+    private void validate() {
+        if (staleRowsFraction != null) {
+            if (!Double.isFinite(staleRowsFraction) || staleRowsFraction > 1 
|| staleRowsFraction < 0) {
+                throw new CatalogValidationException("Stale rows fraction 
should be in range [0, 1].");
+            }
+        }
+
+        if (minStaleRowsCount != null) {
+            if (minStaleRowsCount < 0) {
+                throw new CatalogValidationException("Minimal stale rows count 
should be non-negative.");
+            }
+        }
+    }
+
+    private static class Builder implements 
AlterTableSetPropertyCommandBuilder {
+        private @Nullable Double staleRowsFraction;
+        private @Nullable Long minStaleRowsCount;
+
+        private String schemaName;
+        private String tableName;
+
+        private boolean ifTableExists;
+
+        @Override
+        public AlterTableSetPropertyCommandBuilder schemaName(String 
schemaName) {
+            this.schemaName = schemaName;
+
+            return this;
+        }
+
+        @Override
+        public AlterTableSetPropertyCommandBuilder tableName(String tableName) 
{
+            this.tableName = tableName;
+
+            return this;
+        }
+
+        @Override
+        public AlterTableSetPropertyCommandBuilder ifTableExists(boolean 
ifTableExists) {
+            this.ifTableExists = ifTableExists;
+
+            return this;
+        }
+
+        @Override
+        public AlterTableSetPropertyCommandBuilder staleRowsFraction(double 
staleRowsFraction) {
+            this.staleRowsFraction = staleRowsFraction;
+
+            return this;
+        }
+
+        @Override
+        public AlterTableSetPropertyCommandBuilder minStaleRowsCount(long 
minStaleRowsCount) {
+            this.minStaleRowsCount = minStaleRowsCount;
+
+            return this;
+        }
+
+        @Override
+        public CatalogCommand build() {
+            return new AlterTableSetPropertyCommand(
+                    tableName,
+                    schemaName,
+                    ifTableExists,
+                    staleRowsFraction,
+                    minStaleRowsCount
+            );
+        }
+    }
+}
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableSetPropertyCommandBuilder.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableSetPropertyCommandBuilder.java
new file mode 100644
index 00000000000..52cbefeec09
--- /dev/null
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableSetPropertyCommandBuilder.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.catalog.commands;
+
+/**
+ * Builder of a command that changes table's properties.
+ *
+ * <p>A builder is considered to be reusable, thus implementation have to make 
sure invocation of {@link #build()} method doesn't cause any
+ * side effects on builder's state or any object created by the same builder.
+ */
+public interface AlterTableSetPropertyCommandBuilder extends 
AbstractTableCommandBuilder<AlterTableSetPropertyCommandBuilder> {
+    /** A fraction of a partition to be modified before the data is considered 
to be "stale". Should be in range [0, 1]. */
+    AlterTableSetPropertyCommandBuilder staleRowsFraction(double 
staleRowsFraction);
+
+    /** Minimal number of rows in partition to be modified before the data is 
considered to be "stale". Should be non-negative. */
+    AlterTableSetPropertyCommandBuilder minStaleRowsCount(long 
minStaleRowsCount);
+}
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CatalogUtils.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CatalogUtils.java
index b8b7444eb0e..11bed4f4ee5 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CatalogUtils.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CatalogUtils.java
@@ -178,6 +178,9 @@ public class CatalogUtils {
 
     public static final ConsistencyMode DEFAULT_CONSISTENCY_MODE = 
ConsistencyMode.STRONG_CONSISTENCY;
 
+    public static final long DEFAULT_MIN_STALE_ROWS_COUNT = 500L;
+    public static final double DEFAULT_STALE_ROWS_FRACTION = 0.2d;
+
     private static final Map<ColumnType, Set<ColumnType>> 
ALTER_COLUMN_TYPE_TRANSITIONS = new EnumMap<>(ColumnType.class);
 
     /**
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateTableCommand.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateTableCommand.java
index 5fdb49c48c7..98f357f1d97 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateTableCommand.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateTableCommand.java
@@ -67,7 +67,10 @@ public class CreateTableCommand extends AbstractTableCommand 
{
 
     private final String zoneName;
 
-    private String storageProfile;
+    private final @Nullable String storageProfile;
+
+    private final double staleRowsFraction;
+    private final long minStaleRowsCount;
 
     /**
      * Constructs the object.
@@ -76,11 +79,15 @@ public class CreateTableCommand extends 
AbstractTableCommand {
      * @param schemaName Name of the schema to create table in. Should not be 
null or blank.
      * @param ifNotExists Flag indicating whether the {@code IF NOT EXISTS} 
was specified.
      * @param primaryKey Primary key.
-     * @param colocationColumns Name of the columns participating in 
distribution calculation.
-     *      Should be subset of the primary key columns.
+     * @param colocationColumns Name of the columns participating in 
distribution calculation. Should be subset of the primary key
+     *         columns.
      * @param columns List of the columns containing by the table. There 
should be at least one column.
      * @param zoneName Name of the zone to create table in or {@code null} to 
use the default distribution zone.
      * @param validateSystemSchemas Flag indicating whether system schemas 
should be validated.
+     * @param staleRowsFraction A fraction of a partition to be modified 
before the data is considered to be "stale". Should be in
+     *         range [0, 1].
+     * @param minStaleRowsCount Minimal number of rows in partition to be 
modified before the data is considered to be "stale".
+     *         Should be non-negative.
      * @throws CatalogValidationException if any of restrictions above is 
violated.
      */
     private CreateTableCommand(
@@ -91,8 +98,10 @@ public class CreateTableCommand extends AbstractTableCommand 
{
             List<String> colocationColumns,
             List<ColumnParams> columns,
             @Nullable String zoneName,
-            String storageProfile,
-            boolean validateSystemSchemas
+            @Nullable String storageProfile,
+            boolean validateSystemSchemas,
+            double staleRowsFraction,
+            long minStaleRowsCount
     ) throws CatalogValidationException {
         super(schemaName, tableName, ifNotExists, validateSystemSchemas);
 
@@ -101,6 +110,8 @@ public class CreateTableCommand extends 
AbstractTableCommand {
         this.columns = copyOrNull(columns);
         this.zoneName = zoneName;
         this.storageProfile = storageProfile;
+        this.staleRowsFraction = staleRowsFraction;
+        this.minStaleRowsCount = minStaleRowsCount;
 
         validate();
     }
@@ -127,9 +138,11 @@ public class CreateTableCommand extends 
AbstractTableCommand {
             zone = zone(catalog, zoneName, true);
         }
 
-        if (storageProfile == null) {
-            storageProfile = 
zone.storageProfiles().defaultProfile().storageProfile();
-        }
+        assert zone != null;
+
+        String storageProfile = this.storageProfile != null
+                ? this.storageProfile
+                : zone.storageProfiles().defaultProfile().storageProfile();
 
         ensureZoneContainsTablesStorageProfile(zone, storageProfile);
 
@@ -147,6 +160,8 @@ public class CreateTableCommand extends 
AbstractTableCommand {
                 .primaryKeyColumns(primaryKey.columns())
                 .colocationColumns(colocationColumns)
                 .storageProfile(storageProfile)
+                .minStaleRowsCount(minStaleRowsCount)
+                .staleRowsFraction(staleRowsFraction)
                 .build();
 
         String indexName = primaryKey.name();
@@ -209,6 +224,14 @@ public class CreateTableCommand extends 
AbstractTableCommand {
                 throw new CatalogValidationException("Colocation column '{}' 
specified more that once", name);
             }
         }
+
+        if (!Double.isFinite(staleRowsFraction) || staleRowsFraction > 1 || 
staleRowsFraction < 0) {
+            throw new CatalogValidationException("Stale rows fraction should 
be in range [0, 1].");
+        }
+
+        if (minStaleRowsCount < 0) {
+            throw new CatalogValidationException("Minimal stale rows count 
should be non-negative.");
+        }
     }
 
     private CatalogIndexDescriptor createPkIndexDescriptor(String indexName, 
int pkIndexId, int tableId) {
@@ -274,6 +297,9 @@ public class CreateTableCommand extends 
AbstractTableCommand {
 
         private boolean validateSystemSchemas = true;
 
+        private double staleRowsFraction = 
CatalogUtils.DEFAULT_STALE_ROWS_FRACTION;
+        private long minStaleRowsCount = 
CatalogUtils.DEFAULT_MIN_STALE_ROWS_COUNT;
+
         @Override
         public CreateTableCommandBuilder schemaName(String schemaName) {
             this.schemaName = schemaName;
@@ -337,6 +363,20 @@ public class CreateTableCommand extends 
AbstractTableCommand {
             return this;
         }
 
+        @Override
+        public CreateTableCommandBuilder staleRowsFraction(double 
staleRowsFraction) {
+            this.staleRowsFraction = staleRowsFraction;
+
+            return this;
+        }
+
+        @Override
+        public CreateTableCommandBuilder minStaleRowsCount(long 
minStaleRowsCount) {
+            this.minStaleRowsCount = minStaleRowsCount;
+
+            return this;
+        }
+
         @Override
         public CatalogCommand build() {
             List<String> colocationColumns;
@@ -360,7 +400,9 @@ public class CreateTableCommand extends 
AbstractTableCommand {
                     columns,
                     zoneName,
                     storageProfile,
-                    validateSystemSchemas
+                    validateSystemSchemas,
+                    staleRowsFraction,
+                    minStaleRowsCount
             );
         }
     }
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateTableCommandBuilder.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateTableCommandBuilder.java
index bf754a57563..8fc2d02b82b 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateTableCommandBuilder.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateTableCommandBuilder.java
@@ -50,4 +50,10 @@ public interface CreateTableCommandBuilder extends 
AbstractTableCommandBuilder<C
 
     /** Validate if system schemas are used. */
     CreateTableCommandBuilder validateSystemSchemas(boolean 
validateSystemSchemas);
+
+    /** A fraction of a partition to be modified before the data is considered 
to be "stale". Should be in range [0, 1]. */
+    CreateTableCommandBuilder staleRowsFraction(double staleRowsFraction);
+
+    /** Minimal number of rows in partition to be modified before the data is 
considered to be "stale". Should be non-negative. */
+    CreateTableCommandBuilder minStaleRowsCount(long minStaleRowsCount);
 }
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptor.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptor.java
index 453276d84cf..5a60bb825da 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptor.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptor.java
@@ -64,6 +64,8 @@ public class CatalogTableDescriptor extends 
CatalogObjectDescriptor implements M
 
     private final String storageProfile;
 
+    private final CatalogTableProperties properties;
+
     /**
      * Internal constructor.
      *
@@ -87,7 +89,8 @@ public class CatalogTableDescriptor extends 
CatalogObjectDescriptor implements M
             @Nullable List<String> colocationCols,
             CatalogTableSchemaVersions schemaVersions,
             String storageProfile,
-            HybridTimestamp timestamp
+            HybridTimestamp timestamp,
+            CatalogTableProperties properties
     ) {
         super(id, Type.TABLE, name, timestamp);
 
@@ -107,6 +110,7 @@ public class CatalogTableDescriptor extends 
CatalogObjectDescriptor implements M
         this.colocationColumns = Objects.requireNonNullElse(colocationCols, 
pkCols);
         this.schemaVersions =  Objects.requireNonNull(schemaVersions, "No 
catalog schema versions.");
         this.storageProfile = Objects.requireNonNull(storageProfile, "No 
storage profile.");
+        this.properties = properties;
     }
 
     /**
@@ -126,7 +130,9 @@ public class CatalogTableDescriptor extends 
CatalogObjectDescriptor implements M
                 .columns(columns)
                 .primaryKeyColumns(primaryKeyColumns())
                 .colocationColumns(colocationColumns())
-                .storageProfile(storageProfile());
+                .storageProfile(storageProfile())
+                .staleRowsFraction(properties.staleRowsFraction())
+                .minStaleRowsCount(properties.minStaleRowsCount());
     }
 
     public static Builder builder() {
@@ -241,6 +247,11 @@ public class CatalogTableDescriptor extends 
CatalogObjectDescriptor implements M
         return storageProfile;
     }
 
+    /** Returns holder for table-related properties. */
+    public CatalogTableProperties properties() {
+        return properties;
+    }
+
     /**
      * {@code CatalogTableDescriptor} builder static inner class.
      */
@@ -257,6 +268,8 @@ public class CatalogTableDescriptor extends 
CatalogObjectDescriptor implements M
         private String storageProfile;
         private HybridTimestamp timestamp = INITIAL_TIMESTAMP;
         private int latestSchemaVersion = 0;
+        private double staleRowsFraction;
+        private long minStaleRowsCount;
 
         /**
          * Sets the {@code id} and returns a reference to this Builder 
enabling method chaining.
@@ -390,6 +403,30 @@ public class CatalogTableDescriptor extends 
CatalogObjectDescriptor implements M
             return this;
         }
 
+        /**
+         * Sets the {@code minStaleRowsCount} and returns a reference to this 
Builder enabling method chaining.
+         *
+         * @param minStaleRowsCount The {@code minStaleRowsCount} to set.
+         * @return A reference to this Builder.
+         * @see CatalogTableProperties#minStaleRowsCount()
+         */
+        public Builder minStaleRowsCount(long minStaleRowsCount) {
+            this.minStaleRowsCount = minStaleRowsCount;
+            return this;
+        }
+
+        /**
+         * Sets the {@code staleRowsFraction} and returns a reference to this 
Builder enabling method chaining.
+         *
+         * @param staleRowsFraction The {@code staleRowsFraction} to set.
+         * @return A reference to this Builder.
+         * @see CatalogTableProperties#staleRowsFraction()
+         */
+        public Builder staleRowsFraction(double staleRowsFraction) {
+            this.staleRowsFraction = staleRowsFraction;
+            return this;
+        }
+
         /**
          * Returns a {@code CatalogTableDescriptor} built from the parameters 
previously set.
          *
@@ -449,7 +486,8 @@ public class CatalogTableDescriptor extends 
CatalogObjectDescriptor implements M
                     colocationColumns,
                     newSchemaVersions,
                     storageProfile,
-                    timestamp
+                    timestamp,
+                    new CatalogTableProperties(staleRowsFraction, 
minStaleRowsCount)
             );
         }
     }
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptorSerializers.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptorSerializers.java
index 4195dff0f41..8878f499e40 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptorSerializers.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptorSerializers.java
@@ -202,7 +202,7 @@ public class CatalogTableDescriptorSerializers {
             HybridTimestamp updateTimestamp = updateTimestampLong == 0 ? 
MIN_VALUE : hybridTimestamp(updateTimestampLong);
 
             CatalogTableSchemaVersions schemaVersions =  
input.readEntry(CatalogTableSchemaVersions.class);
-            List<CatalogTableColumnDescriptor> columns = 
input.readEntryList(CatalogTableColumnDescriptor.class);
+            List<CatalogTableColumnDescriptor> columns = 
schemaVersions.get(schemaVersions.latestVersion()).columns();
             String storageProfile = input.readUTF();
 
             int schemaId = input.readVarIntAsInt();
@@ -233,6 +233,9 @@ public class CatalogTableDescriptorSerializers {
                 }
             }
 
+            double staleRowsFraction = input.readDouble();
+            long minStaleRowsCount = input.readVarInt();
+
             return CatalogTableDescriptor.builder()
                     .id(id)
                     .schemaId(schemaId)
@@ -245,6 +248,8 @@ public class CatalogTableDescriptorSerializers {
                     .schemaVersions(schemaVersions)
                     .storageProfile(storageProfile)
                     .timestamp(updateTimestamp)
+                    .staleRowsFraction(staleRowsFraction)
+                    .minStaleRowsCount(minStaleRowsCount)
                     .build();
         }
 
@@ -255,7 +260,6 @@ public class CatalogTableDescriptorSerializers {
             output.writeVarInt(descriptor.updateTimestamp().longValue());
 
             output.writeEntry(descriptor.schemaVersions());
-            output.writeEntryList(descriptor.columns());
             output.writeUTF(descriptor.storageProfile());
 
             output.writeVarInt(descriptor.schemaId());
@@ -275,6 +279,9 @@ public class CatalogTableDescriptorSerializers {
                 output.writeVarInt(colocationIndexes.length);
                 output.writeIntArray(colocationIndexes);
             }
+
+            output.writeDouble(descriptor.properties().staleRowsFraction());
+            output.writeVarInt(descriptor.properties().minStaleRowsCount());
         }
 
         private static int[] resolveColocationColumnIndexes(int[] 
pkColumnIndexes, CatalogTableDescriptor descriptor) {
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableProperties.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableProperties.java
new file mode 100644
index 00000000000..0f51de7f579
--- /dev/null
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableProperties.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.catalog.descriptors;
+
+/**
+ * This class encapsulates different properties of a table.
+ *
+ * <p>Put here configuration that is required by auxiliary systems, but not 
defines how and where to store the data.
+ */
+public class CatalogTableProperties {
+    private final double staleRowsFraction;
+    private final long minStaleRowsCount;
+
+    CatalogTableProperties(double staleRowsFraction, long minStaleRowsCount) {
+        this.staleRowsFraction = staleRowsFraction;
+        this.minStaleRowsCount = minStaleRowsCount;
+    }
+
+    /**
+     * Returns {@code double} value in the range [0.0, 1] representing 
fraction of a partition to be modified before the data is considered
+     * to be "stale". That is, any computation made on a data snapshot is 
considered obsolete and need to be refreshed.
+     */
+    public double staleRowsFraction() {
+        return staleRowsFraction;
+    }
+
+    /**
+     * Returns minimal number of rows in partition to be modified before the 
data is considered to be "stale". That is, any computation made
+     * on a data snapshot is considered obsolete and need to be refreshed.
+     */
+    public long minStaleRowsCount() {
+        return minStaleRowsCount;
+    }
+}
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/events/AlterTablePropertiesEventParameters.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/events/AlterTablePropertiesEventParameters.java
new file mode 100644
index 00000000000..9dd7fe9c716
--- /dev/null
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/events/AlterTablePropertiesEventParameters.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.catalog.events;
+
+import org.apache.ignite.internal.catalog.storage.AlterTablePropertiesEntry;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Alter table properties event parameters.
+ */
+public class AlterTablePropertiesEventParameters extends TableEventParameters {
+
+    private final AlterTablePropertiesEntry entry;
+
+    /**
+     * Constructor.
+     *
+     * @param causalityToken Causality token.
+     * @param catalogVersion Catalog version.
+     * @param entry An entry which causes this event.
+     */
+    public AlterTablePropertiesEventParameters(
+            long causalityToken,
+            int catalogVersion,
+            AlterTablePropertiesEntry entry
+    ) {
+        super(causalityToken, catalogVersion, entry.tableId());
+
+        this.entry = entry;
+    }
+
+    /**
+     * Returns {@code double} value in the range [0.0, 1] representing 
fraction of a partition to be modified before the data is considered
+     * to be "stale", or {@code null} if this parameter didn't change.
+     */
+    public @Nullable Double staleRowsFraction() {
+        return entry.staleRowsFraction();
+    }
+
+    /**
+     * Returns minimal number of rows in partition to be modified before the 
data is considered to be "stale", or {@code null} if this
+     * parameter didn't change.
+     */
+    public @Nullable Long minStaleRowsCount() {
+        return entry.minStaleRowsCount();
+    }
+}
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterTablePropertiesEntry.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterTablePropertiesEntry.java
new file mode 100644
index 00000000000..8b9d6f58665
--- /dev/null
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterTablePropertiesEntry.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.catalog.storage;
+
+import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
+import 
org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor.Builder;
+import 
org.apache.ignite.internal.catalog.events.AlterTablePropertiesEventParameters;
+import org.apache.ignite.internal.catalog.events.CatalogEvent;
+import org.apache.ignite.internal.catalog.events.CatalogEventParameters;
+import 
org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
+import org.apache.ignite.internal.tostring.S;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Describes changing of table properties.
+ */
+public class AlterTablePropertiesEntry extends UpdateTable implements Fireable 
{
+    private final int tableId;
+
+    private final @Nullable Double staleRowsFraction;
+    private final @Nullable Long minStaleRowsCount;
+
+    /**
+     * Constructs the object.
+     *
+     * @param tableId Table id.
+     * @param staleRowsFraction Stale rows fraction.
+     * @param minStaleRowsCount Minimum stale rows count
+     */
+    public AlterTablePropertiesEntry(
+            int tableId,
+            @Nullable Double staleRowsFraction,
+            @Nullable Long minStaleRowsCount
+    ) {
+        this.tableId = tableId;
+        this.staleRowsFraction = staleRowsFraction;
+        this.minStaleRowsCount = minStaleRowsCount;
+    }
+
+    /** Returns table id. */
+    @Override
+    public int tableId() {
+        return tableId;
+    }
+
+    @Override
+    public int typeId() {
+        return MarshallableEntryType.ALTER_TABLE_PROPERTIES.id();
+    }
+
+    @Override
+    public CatalogEvent eventType() {
+        return CatalogEvent.TABLE_ALTER;
+    }
+
+    @Override
+    public CatalogEventParameters createEventParameters(long causalityToken, 
int catalogVersion) {
+        return new AlterTablePropertiesEventParameters(causalityToken, 
catalogVersion, this);
+    }
+
+    @Override
+    public Builder newTableDescriptor(CatalogTableDescriptor table) {
+        Builder builder = table.copyBuilder();
+
+        if (minStaleRowsCount != null) {
+            builder.minStaleRowsCount(minStaleRowsCount);
+        }
+
+        if (staleRowsFraction != null) {
+            builder.staleRowsFraction(staleRowsFraction);
+        }
+
+        return builder;
+    }
+
+    public @Nullable Double staleRowsFraction() {
+        return staleRowsFraction;
+    }
+
+    public @Nullable Long minStaleRowsCount() {
+        return minStaleRowsCount;
+    }
+
+    @Override
+    public String toString() {
+        return S.toString(this);
+    }
+}
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterTablePropertiesEntrySerializers.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterTablePropertiesEntrySerializers.java
new file mode 100644
index 00000000000..b072935ba4f
--- /dev/null
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterTablePropertiesEntrySerializers.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.catalog.storage;
+
+import static 
org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializationUtils.readNullableDouble;
+import static 
org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializationUtils.readNullableLong;
+import static 
org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializationUtils.writeNullableDouble;
+import static 
org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializationUtils.writeNullableLong;
+
+import java.io.IOException;
+import 
org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import 
org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import 
org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
+import 
org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
+import 
org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
+
+/**
+ * Serializers for {@link AlterTablePropertiesEntry}.
+ */
+public class AlterTablePropertiesEntrySerializers {
+    /**
+     * Serializer for {@link AlterTablePropertiesEntry}.
+     */
+    @CatalogSerializer(version = 1, since = "3.1.0")
+    static class AlterTablePropertiesEntrySerializer implements 
CatalogObjectSerializer<AlterTablePropertiesEntry> {
+        private final CatalogEntrySerializerProvider serializers;
+
+        public 
AlterTablePropertiesEntrySerializer(CatalogEntrySerializerProvider serializers) 
{
+            this.serializers = serializers;
+        }
+
+        @Override
+        public AlterTablePropertiesEntry readFrom(CatalogObjectDataInput 
input) throws IOException {
+            int tableId = input.readVarIntAsInt();
+            Double staleRowsFraction = readNullableDouble(input);
+            Long minStaleRowsCount = readNullableLong(input);
+
+            return new AlterTablePropertiesEntry(tableId, staleRowsFraction, 
minStaleRowsCount);
+        }
+
+        @Override
+        public void writeTo(AlterTablePropertiesEntry value, 
CatalogObjectDataOutput output) throws IOException {
+            output.writeVarInt(value.tableId());
+            writeNullableDouble(value.staleRowsFraction(), output);
+            writeNullableLong(value.minStaleRowsCount(), output);
+        }
+    }
+}
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogSerializationUtils.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogSerializationUtils.java
index 899301e7cf3..c4b4058ca7d 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogSerializationUtils.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogSerializationUtils.java
@@ -124,6 +124,42 @@ public class CatalogSerializationUtils {
         return entries;
     }
 
+    /** Reads nullable long. */
+    public static @Nullable Long readNullableLong(DataInput in) throws 
IOException {
+        if (!in.readBoolean()) {
+            return null;
+        }
+
+        return in.readLong();
+    }
+
+    /** Reads nullable double. */
+    public static @Nullable Double readNullableDouble(DataInput in) throws 
IOException {
+        if (!in.readBoolean()) {
+            return null;
+        }
+
+        return in.readDouble();
+    }
+
+    /** Writes nullable long. */
+    public static void writeNullableLong(@Nullable Long value, DataOutput out) 
throws IOException {
+        out.writeBoolean(value != null);
+
+        if (value != null) {
+            out.writeLong(value);
+        }
+    }
+
+    /** Writes nullable long. */
+    public static void writeNullableDouble(@Nullable Double value, DataOutput 
out) throws IOException {
+        out.writeBoolean(value != null);
+
+        if (value != null) {
+            out.writeDouble(value);
+        }
+    }
+
     /** Writes list of objects. */
     public static <T> void writeList(List<T> items, CatalogObjectSerializer<T> 
serializer,
             CatalogObjectDataOutput output) throws IOException {
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/MarshallableEntryType.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/MarshallableEntryType.java
index f1cf7518068..e4cd327f385 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/MarshallableEntryType.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/MarshallableEntryType.java
@@ -31,6 +31,7 @@ import 
org.apache.ignite.internal.catalog.descriptors.CatalogTableSchemaVersions
 import 
org.apache.ignite.internal.catalog.descriptors.CatalogTableVersionSerializers;
 import 
org.apache.ignite.internal.catalog.descriptors.CatalogZoneDescriptorSerializers;
 import org.apache.ignite.internal.catalog.storage.AlterColumnEntrySerializers;
+import 
org.apache.ignite.internal.catalog.storage.AlterTablePropertiesEntrySerializers;
 import org.apache.ignite.internal.catalog.storage.AlterZoneEntrySerializers;
 import org.apache.ignite.internal.catalog.storage.DropColumnsEntrySerializers;
 import org.apache.ignite.internal.catalog.storage.DropIndexEntrySerializers;
@@ -90,7 +91,8 @@ public enum MarshallableEntryType implements 
CatalogSerializerTypeDefinition {
     DESCRIPTOR_TABLE_COLUMN(29, CatalogTableColumnDescriptorSerializers.class),
     DESCRIPTOR_TABLE_VERSION(30, CatalogTableVersionSerializers.class),
     DESCRIPTOR_TABLE_SCHEMA_VERSIONS(31, 
CatalogTableSchemaVersionsSerializers.class),
-    DESCRIPTOR_ZONE(32, CatalogZoneDescriptorSerializers.class);
+    DESCRIPTOR_ZONE(32, CatalogZoneDescriptorSerializers.class),
+    ALTER_TABLE_PROPERTIES(33, AlterTablePropertiesEntrySerializers.class);
 
     /** Type ID. */
     private final int id;
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogTableTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogTableTest.java
index bd4f80eea30..ad21ee926de 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogTableTest.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogTableTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.catalog;
 
+import static java.lang.Double.compare;
 import static java.util.stream.Collectors.toList;
 import static 
org.apache.ignite.internal.catalog.CatalogManagerImpl.DEFAULT_ZONE_NAME;
 import static 
org.apache.ignite.internal.catalog.CatalogTestUtils.addColumnParams;
@@ -73,9 +74,11 @@ import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
+import java.util.function.Predicate;
 import java.util.function.Supplier;
 import 
org.apache.ignite.internal.catalog.commands.AlterTableAlterColumnCommand;
 import 
org.apache.ignite.internal.catalog.commands.AlterTableAlterColumnCommandBuilder;
+import 
org.apache.ignite.internal.catalog.commands.AlterTableSetPropertyCommand;
 import org.apache.ignite.internal.catalog.commands.CatalogUtils;
 import org.apache.ignite.internal.catalog.commands.ColumnParams;
 import org.apache.ignite.internal.catalog.commands.ColumnParams.Builder;
@@ -102,6 +105,8 @@ import 
org.apache.ignite.internal.catalog.events.RenameTableEventParameters;
 import org.apache.ignite.internal.event.EventListener;
 import org.apache.ignite.internal.sql.SqlCommon;
 import org.apache.ignite.sql.ColumnType;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
 import org.hamcrest.TypeSafeMatcher;
 import org.jetbrains.annotations.Nullable;
 import org.junit.jupiter.api.Test;
@@ -1235,6 +1240,160 @@ public class CatalogTableTest extends 
BaseCatalogManagerTest {
         assertThat(catalog.tables(zoneDescriptor.id()).stream().map(d -> 
d.id()).collect(toList()), hasItem(customTableId));
     }
 
+    @Test
+    void createTableWithStalenessConfiguration() {
+        {
+            CatalogCommand tableCmdWithDefault = createTableCommandBuilder(
+                    SqlCommon.DEFAULT_SCHEMA_NAME,
+                    "defaults",
+                    List.of(columnParams("ID", INT32), columnParams("VAL", 
INT32)),
+                    List.of("ID"),
+                    null)
+                    .build();
+            tryApplyAndExpectApplied(tableCmdWithDefault);
+        }
+
+        {
+            CatalogCommand tableCmdWithOnlyRowFraction = 
createTableCommandBuilder(
+                    SqlCommon.DEFAULT_SCHEMA_NAME,
+                    "stale_rows",
+                    List.of(columnParams("ID", INT32), columnParams("VAL", 
INT32)),
+                    List.of("ID"),
+                    null)
+                    .staleRowsFraction(0.8)
+                    .build();
+            tryApplyAndExpectApplied(tableCmdWithOnlyRowFraction);
+        }
+
+        {
+            CatalogCommand tableCmdWithOnlyMinRowCount = 
createTableCommandBuilder(
+                    SqlCommon.DEFAULT_SCHEMA_NAME,
+                    "min_rows",
+                    List.of(columnParams("ID", INT32), columnParams("VAL", 
INT32)),
+                    List.of("ID"),
+                    null)
+                    .minStaleRowsCount(2000L)
+                    .build();
+            tryApplyAndExpectApplied(tableCmdWithOnlyMinRowCount);
+        }
+
+        {
+            CatalogCommand tableCmdWithEverything = createTableCommandBuilder(
+                    SqlCommon.DEFAULT_SCHEMA_NAME,
+                    "everything",
+                    List.of(columnParams("ID", INT32), columnParams("VAL", 
INT32)),
+                    List.of("ID"),
+                    null)
+                    .minStaleRowsCount(4000L)
+                    .staleRowsFraction(0.5)
+                    .build();
+            tryApplyAndExpectApplied(tableCmdWithEverything);
+        }
+
+        assertThat(manager.catalog(manager.latestCatalogVersion()).tables(), 
hasItems(
+                tableThatSatisfies("table with stale rows conf that matches 
defaults", d -> 
+                        "defaults".equals(d.name())
+                                && d.properties().minStaleRowsCount() == 
CatalogUtils.DEFAULT_MIN_STALE_ROWS_COUNT
+                                && compare(d.properties().staleRowsFraction(), 
CatalogUtils.DEFAULT_STALE_ROWS_FRACTION) == 0),
+                tableThatSatisfies("table with non-default stale rows fraction 
conf", d ->
+                        "stale_rows".equals(d.name())
+                                && d.properties().minStaleRowsCount() == 
CatalogUtils.DEFAULT_MIN_STALE_ROWS_COUNT
+                                && compare(d.properties().staleRowsFraction(), 
0.8) == 0),
+                tableThatSatisfies("table with non-default min stale rows 
conf", d ->
+                        "min_rows".equals(d.name())
+                                && d.properties().minStaleRowsCount() == 2000L
+                                && compare(d.properties().staleRowsFraction(), 
CatalogUtils.DEFAULT_STALE_ROWS_FRACTION) == 0),
+                tableThatSatisfies("table with non-deafult stale rows con", d 
->
+                        "everything".equals(d.name())
+                                && d.properties().minStaleRowsCount() == 4000L
+                                && compare(d.properties().staleRowsFraction(), 
0.5) == 0)
+        ));
+    }
+
+    @Test
+    void alterTableStalenessConfiguration() {
+        { // create table with default row staleness configuration
+            CatalogCommand tableCmdWithDefault = createTableCommandBuilder(
+                    SqlCommon.DEFAULT_SCHEMA_NAME,
+                    TABLE_NAME,
+                    List.of(columnParams("ID", INT32), columnParams("VAL", 
INT32)),
+                    List.of("ID"),
+                    null)
+                    .build();
+            tryApplyAndExpectApplied(tableCmdWithDefault);
+
+            assertThat(
+                    actualTable(TABLE_NAME),
+                    tableThatSatisfies("table with default configuration", d ->
+                            d.properties().minStaleRowsCount() == 
CatalogUtils.DEFAULT_MIN_STALE_ROWS_COUNT
+                                    && 
compare(d.properties().staleRowsFraction(), 
CatalogUtils.DEFAULT_STALE_ROWS_FRACTION) == 0)
+            );
+        }
+
+        { // let's change row fraction first
+            CatalogCommand tableCmdWithDefault = 
AlterTableSetPropertyCommand.builder()
+                    .schemaName(SqlCommon.DEFAULT_SCHEMA_NAME)
+                    .tableName(TABLE_NAME)
+                    
.staleRowsFraction(CatalogUtils.DEFAULT_STALE_ROWS_FRACTION * 2)
+                    .build();
+            tryApplyAndExpectApplied(tableCmdWithDefault);
+
+            assertThat(
+                    actualTable(TABLE_NAME),
+                    tableThatSatisfies("table with default configuration", d ->
+                            d.properties().minStaleRowsCount() == 
CatalogUtils.DEFAULT_MIN_STALE_ROWS_COUNT
+                                    && 
compare(d.properties().staleRowsFraction(), 
CatalogUtils.DEFAULT_STALE_ROWS_FRACTION * 2) == 0)
+            );
+        }
+
+        { // now let's change min rows count
+            CatalogCommand tableCmdWithDefault = 
AlterTableSetPropertyCommand.builder()
+                    .schemaName(SqlCommon.DEFAULT_SCHEMA_NAME)
+                    .tableName(TABLE_NAME)
+                    
.minStaleRowsCount(CatalogUtils.DEFAULT_MIN_STALE_ROWS_COUNT * 2)
+                    .build();
+            tryApplyAndExpectApplied(tableCmdWithDefault);
+
+            assertThat(
+                    actualTable(TABLE_NAME),
+                    tableThatSatisfies("table with default configuration", d ->
+                            d.properties().minStaleRowsCount() == 
CatalogUtils.DEFAULT_MIN_STALE_ROWS_COUNT * 2
+                                    && 
compare(d.properties().staleRowsFraction(), 
CatalogUtils.DEFAULT_STALE_ROWS_FRACTION * 2) == 0)
+            );
+        }
+
+        { // finally, let's change both back to defaults
+            CatalogCommand tableCmdWithDefault = 
AlterTableSetPropertyCommand.builder()
+                    .schemaName(SqlCommon.DEFAULT_SCHEMA_NAME)
+                    .tableName(TABLE_NAME)
+                    
.minStaleRowsCount(CatalogUtils.DEFAULT_MIN_STALE_ROWS_COUNT)
+                    
.staleRowsFraction(CatalogUtils.DEFAULT_STALE_ROWS_FRACTION)
+                    .build();
+            tryApplyAndExpectApplied(tableCmdWithDefault);
+
+            assertThat(
+                    actualTable(TABLE_NAME),
+                    tableThatSatisfies("table with default configuration", d ->
+                            d.properties().minStaleRowsCount() == 
CatalogUtils.DEFAULT_MIN_STALE_ROWS_COUNT
+                                    && 
compare(d.properties().staleRowsFraction(), 
CatalogUtils.DEFAULT_STALE_ROWS_FRACTION) == 0)
+            );
+        }
+    }
+
+    private static BaseMatcher<CatalogTableDescriptor> 
tableThatSatisfies(String description, Predicate<CatalogTableDescriptor> 
predicate) {
+        return new BaseMatcher<>() {
+            @Override
+            public boolean matches(Object o) {
+                return o instanceof CatalogTableDescriptor && 
predicate.test((CatalogTableDescriptor) o);
+            }
+
+            @Override
+            public void describeTo(Description description0) {
+                description0.appendText(description);
+            }
+        };
+    }
+
     private CompletableFuture<CatalogApplyResult> changeColumn(
             String tab,
             String col,
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/AlterTableSetPropertyCommandValidationTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/AlterTableSetPropertyCommandValidationTest.java
new file mode 100644
index 00000000000..19bc2216416
--- /dev/null
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/AlterTableSetPropertyCommandValidationTest.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.catalog.commands;
+
+import static 
org.apache.ignite.internal.testframework.IgniteTestUtils.assertThrowsWithCause;
+
+import org.apache.ignite.internal.catalog.Catalog;
+import org.apache.ignite.internal.catalog.CatalogCommand;
+import org.apache.ignite.internal.catalog.CatalogValidationException;
+import org.apache.ignite.internal.catalog.UpdateContext;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+/**
+ * Tests to verify validation of {@link AlterTableSetPropertyCommand}.
+ */
+@SuppressWarnings("ThrowableNotThrown")
+public class AlterTableSetPropertyCommandValidationTest extends 
AbstractCommandValidationTest {
+    @ParameterizedTest(name = "[{index}] ''{argumentsWithNames}''")
+    @MethodSource("nullAndBlankStrings")
+    void schemaNameMustNotBeNullOrBlank(String name) {
+        AlterTableSetPropertyCommandBuilder builder = 
AlterTableSetPropertyCommand.builder();
+
+        builder = fillProperties(builder);
+
+        builder.schemaName(name);
+
+        assertThrowsWithCause(
+                builder::build,
+                CatalogValidationException.class,
+                "Name of the schema can't be null or blank"
+        );
+    }
+
+    @ParameterizedTest(name = "[{index}] ''{argumentsWithNames}''")
+    @MethodSource("nullAndBlankStrings")
+    void tableNameMustNotBeNullOrBlank(String name) {
+        AlterTableSetPropertyCommandBuilder builder = 
AlterTableSetPropertyCommand.builder();
+
+        builder = fillProperties(builder);
+
+        builder.tableName(name);
+
+        assertThrowsWithCause(
+                builder::build,
+                CatalogValidationException.class,
+                "Name of the table can't be null or blank"
+        );
+    }
+
+    @ParameterizedTest
+    @ValueSource(doubles = {
+            Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 
-1, 1.1
+    })
+    void staleRowsFractionShouldBeInValidRange(double staleRowsFraction) {
+        AlterTableSetPropertyCommandBuilder builder = 
AlterTableSetPropertyCommand.builder();
+
+        builder = fillProperties(builder)
+                .staleRowsFraction(staleRowsFraction);
+
+        assertThrowsWithCause(
+                builder::build,
+                CatalogValidationException.class,
+                "Stale rows fraction should be in range [0, 1]."
+        );
+    }
+
+    @ParameterizedTest
+    @ValueSource(longs = {
+            -100, -10, -1
+    })
+    void minStaleRowsCountShouldBeNonNegative(long minStaleRowsCount) {
+        AlterTableSetPropertyCommandBuilder builder = 
AlterTableSetPropertyCommand.builder();
+
+        builder = fillProperties(builder)
+                .minStaleRowsCount(minStaleRowsCount);
+
+        assertThrowsWithCause(
+                builder::build,
+                CatalogValidationException.class,
+                "Minimal stale rows count should be non-negative."
+        );
+    }
+
+    private static AlterTableSetPropertyCommandBuilder 
fillProperties(AlterTableSetPropertyCommandBuilder builder) {
+        return builder
+                .schemaName(SCHEMA_NAME)
+                .tableName("TEST");
+    }
+
+    @Test
+    void exceptionIsThrownIfSchemaNotExists() {
+        AlterTableSetPropertyCommandBuilder builder = 
AlterTableSetPropertyCommand.builder();
+
+        Catalog catalog = catalogWithDefaultZone();
+
+        CatalogCommand command = 
fillProperties(builder).schemaName(SCHEMA_NAME + "_UNK").build();
+
+        assertThrowsWithCause(
+                () -> command.get(new UpdateContext(catalog)),
+                CatalogValidationException.class,
+                "Schema with name 'PUBLIC_UNK' not found"
+        );
+
+        CatalogCommand alterCommand = builder.ifTableExists(true).build();
+
+        alterCommand.get(new UpdateContext(catalog)); // No exception
+    }
+
+    @Test
+    void exceptionIsThrownIfTableNotExists() {
+        AlterTableSetPropertyCommandBuilder builder = 
AlterTableSetPropertyCommand.builder();
+
+        Catalog catalog = catalogWithDefaultZone();
+
+        CatalogCommand command = 
fillProperties(builder).tableName("TEST").build();
+
+        assertThrowsWithCause(
+                () -> command.get(new UpdateContext(catalog)),
+                CatalogValidationException.class,
+                "Table with name 'PUBLIC.TEST' not found"
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("reservedSchemaNames")
+    void exceptionIsThrownIfSchemaIsReserved(String schema) {
+        AlterTableSetPropertyCommandBuilder builder = 
AlterTableSetPropertyCommand.builder();
+
+        builder.schemaName(schema)
+                .tableName("t");
+
+        assertThrowsWithCause(
+                builder::build,
+                CatalogValidationException.class,
+                "Operations with system schemas are not allowed"
+        );
+    }
+}
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CreateTableCommandValidationTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CreateTableCommandValidationTest.java
index 067f3644136..deaa7abffb5 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CreateTableCommandValidationTest.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CreateTableCommandValidationTest.java
@@ -38,11 +38,12 @@ import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.EnumSource;
 import org.junit.jupiter.params.provider.EnumSource.Mode;
 import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
 
 /**
  * Tests to verify validation of {@link CreateTableCommand}.
  */
-@SuppressWarnings({"DataFlowIssue", "ThrowableNotThrown"})
+@SuppressWarnings("ThrowableNotThrown")
 public class CreateTableCommandValidationTest extends 
AbstractCommandValidationTest {
     @ParameterizedTest(name = "[{index}] ''{argumentsWithNames}''")
     @MethodSource("nullAndBlankStrings")
@@ -326,6 +327,40 @@ public class CreateTableCommandValidationTest extends 
AbstractCommandValidationT
         );
     }
 
+    @ParameterizedTest
+    @ValueSource(doubles = {
+            Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 
-1, 1.1
+    })
+    void staleRowsFractionShouldBeInValidRange(double staleRowsFraction) {
+        CreateTableCommandBuilder builder = CreateTableCommand.builder();
+
+        builder = fillProperties(builder)
+                .staleRowsFraction(staleRowsFraction);
+
+        assertThrowsWithCause(
+                builder::build,
+                CatalogValidationException.class,
+                "Stale rows fraction should be in range [0, 1]."
+        );
+    }
+
+    @ParameterizedTest
+    @ValueSource(longs = {
+            -100, -10, -1
+    })
+    void minStaleRowsCountShouldBeNonNegative(long minStaleRowsCount) {
+        CreateTableCommandBuilder builder = CreateTableCommand.builder();
+
+        builder = fillProperties(builder)
+                .minStaleRowsCount(minStaleRowsCount);
+
+        assertThrowsWithCause(
+                builder::build,
+                CatalogValidationException.class,
+                "Minimal stale rows count should be non-negative."
+        );
+    }
+
     @Test
     void exceptionIsThrownIfZoneNeitherSpecifiedExplicitlyNorDefaultWasSet() {
         CreateTableCommandBuilder builder = CreateTableCommand.builder();
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogEntrySerializationTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogEntrySerializationTest.java
index f1535ce7e5a..c8c2e140247 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogEntrySerializationTest.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogEntrySerializationTest.java
@@ -20,10 +20,14 @@ package org.apache.ignite.internal.catalog.storage;
 import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.DEFAULT_FILTER;
 import static org.apache.ignite.internal.hlc.HybridTimestamp.hybridTimestamp;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.empty;
 import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
+import it.unimi.dsi.fastutil.shorts.ShortArrayList;
+import it.unimi.dsi.fastutil.shorts.ShortList;
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.time.LocalDate;
@@ -61,6 +65,8 @@ import 
org.apache.ignite.internal.catalog.descriptors.ConsistencyMode;
 import 
org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
 import 
org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
 import 
org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
+import 
org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
+import 
org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
 import 
org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntry;
 import 
org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
 import 
org.apache.ignite.internal.catalog.storage.serialization.UpdateLogMarshaller;
@@ -100,7 +106,23 @@ public class CatalogEntrySerializationTest extends 
BaseIgniteAbstractTest {
     private static Stream<Arguments> marshallableEntryTypes() {
         return Arrays.stream(MarshallableEntryType.values())
                 .filter(t -> t != MarshallableEntryType.VERSIONED_UPDATE)
-                .flatMap(t -> Stream.of(Arguments.of(t, 1), Arguments.of(t, 
2)));
+                .flatMap(t -> 
resolveVersions(t.container()).intStream().mapToObj(v -> Arguments.of(t, v)));
+    }
+
+    private static ShortList resolveVersions(Class<?> clazz) {
+        ShortList versions = new ShortArrayList();
+
+        for (Class<?> declaredClass : clazz.getDeclaredClasses()) {
+            if (CatalogObjectSerializer.class.isAssignableFrom(declaredClass)) 
{
+                CatalogSerializer catalogSerializer = 
declaredClass.getAnnotation(CatalogSerializer.class);
+
+                versions.add(catalogSerializer.version());
+            }
+        }
+
+        assertThat(versions, not(empty()));
+
+        return versions;
     }
 
     @ParameterizedTest
@@ -254,6 +276,13 @@ public class CatalogEntrySerializationTest extends 
BaseIgniteAbstractTest {
                 
checkDescriptorSerialization(newCatalogZoneDescriptor("myZone", profiles));
                 break;
 
+            case ALTER_TABLE_PROPERTIES:
+                checkSerialization(version, new AlterTablePropertiesEntry(123, 
null, null));
+                checkSerialization(version, new AlterTablePropertiesEntry(123, 
0.2, null));
+                checkSerialization(version, new AlterTablePropertiesEntry(123, 
null, 500L));
+                checkSerialization(version, new AlterTablePropertiesEntry(123, 
0.2, 500L));
+                break;
+
             default:
                 throw new UnsupportedOperationException("Test not implemented 
" + type);
         }
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogSerializationCompatibilityV2ReadsV1Test.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogSerializationCompatibilityV2ReadsV1Test.java
index b4cabb46ba3..ffa161ba32e 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogSerializationCompatibilityV2ReadsV1Test.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogSerializationCompatibilityV2ReadsV1Test.java
@@ -17,6 +17,9 @@
 
 package org.apache.ignite.internal.catalog.storage;
 
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
 /**
  * Tests for catalog storage objects. Protocol version 2 reads protocol 1.
  */
@@ -41,4 +44,16 @@ public class CatalogSerializationCompatibilityV2ReadsV1Test 
extends CatalogSeria
     protected boolean expectExactVersion() {
         return false;
     }
+
+    @Test
+    public void alterTableProperties() {
+        // TODO: https://issues.apache.org/jira/browse/IGNITE-26632 move to 
CatalogSerializationCompatibilityV2ReadsV2Test
+        List<UpdateEntry> entries = List.of(
+                new AlterTablePropertiesEntry(state.id(), null, null),
+                new AlterTablePropertiesEntry(state.id(), 1.0d, null),
+                new AlterTablePropertiesEntry(state.id(), null, 10L),
+                new AlterTablePropertiesEntry(state.id(), 2.0d, 10L)
+        );
+        checker.compareEntries(entries, "AlterTableProperties", 
entryVersion());
+    }
 }
diff --git 
a/modules/catalog/src/test/resources/serialization_v1/AlterTableProperties_1.bin
 
b/modules/catalog/src/test/resources/serialization_v1/AlterTableProperties_1.bin
new file mode 100644
index 00000000000..e69eb12951a
Binary files /dev/null and 
b/modules/catalog/src/test/resources/serialization_v1/AlterTableProperties_1.bin
 differ
diff --git 
a/modules/catalog/src/test/resources/serialization_v2/NewSchemaEntry_2.bin 
b/modules/catalog/src/test/resources/serialization_v2/NewSchemaEntry_2.bin
index 098148e8710..4bc3b3c8307 100644
Binary files 
a/modules/catalog/src/test/resources/serialization_v2/NewSchemaEntry_2.bin and 
b/modules/catalog/src/test/resources/serialization_v2/NewSchemaEntry_2.bin 
differ
diff --git 
a/modules/catalog/src/test/resources/serialization_v2/NewTableEntry_2.bin 
b/modules/catalog/src/test/resources/serialization_v2/NewTableEntry_2.bin
index a5e8521c204..50e9f6e2039 100644
Binary files 
a/modules/catalog/src/test/resources/serialization_v2/NewTableEntry_2.bin and 
b/modules/catalog/src/test/resources/serialization_v2/NewTableEntry_2.bin differ
diff --git 
a/modules/catalog/src/test/resources/serialization_v2/SnapshotEntryNoDefaultZone_2.bin
 
b/modules/catalog/src/test/resources/serialization_v2/SnapshotEntryNoDefaultZone_2.bin
index e18849dc603..ba733857857 100644
Binary files 
a/modules/catalog/src/test/resources/serialization_v2/SnapshotEntryNoDefaultZone_2.bin
 and 
b/modules/catalog/src/test/resources/serialization_v2/SnapshotEntryNoDefaultZone_2.bin
 differ
diff --git 
a/modules/catalog/src/test/resources/serialization_v2/SnapshotEntry_2.bin 
b/modules/catalog/src/test/resources/serialization_v2/SnapshotEntry_2.bin
index 1e9163feac3..a0c41701c3f 100644
Binary files 
a/modules/catalog/src/test/resources/serialization_v2/SnapshotEntry_2.bin and 
b/modules/catalog/src/test/resources/serialization_v2/SnapshotEntry_2.bin differ
diff --git 
a/modules/index/src/test/java/org/apache/ignite/internal/index/IndexManagerTest.java
 
b/modules/index/src/test/java/org/apache/ignite/internal/index/IndexManagerTest.java
index 2c995d3664e..23cddb6f320 100644
--- 
a/modules/index/src/test/java/org/apache/ignite/internal/index/IndexManagerTest.java
+++ 
b/modules/index/src/test/java/org/apache/ignite/internal/index/IndexManagerTest.java
@@ -76,6 +76,7 @@ import org.apache.ignite.internal.table.TableTestUtils;
 import org.apache.ignite.internal.table.TableViewInternal;
 import org.apache.ignite.internal.table.distributed.PartitionSet;
 import org.apache.ignite.internal.table.distributed.TableManager;
+import 
org.apache.ignite.internal.table.distributed.TableStatsStalenessConfiguration;
 import 
org.apache.ignite.internal.table.distributed.schema.ConstantSchemaVersions;
 import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
 import org.apache.ignite.internal.testframework.ExecutorServiceExtension;
@@ -217,7 +218,8 @@ public class IndexManagerTest extends 
BaseIgniteAbstractTest {
                 marshallers,
                 mock(IgniteSql.class),
                 mock(FailureProcessor.class),
-                table.primaryKeyIndexId()
+                table.primaryKeyIndexId(),
+                mock(TableStatsStalenessConfiguration.class)
         ));
     }
 
diff --git 
a/modules/table/src/integrationTest/java/org/apache/ignite/internal/table/ItPartitionModificationCounterMetricsTest.java
 
b/modules/table/src/integrationTest/java/org/apache/ignite/internal/table/ItPartitionModificationCounterMetricsTest.java
index 6c387d0b0b5..50ecf700ce5 100644
--- 
a/modules/table/src/integrationTest/java/org/apache/ignite/internal/table/ItPartitionModificationCounterMetricsTest.java
+++ 
b/modules/table/src/integrationTest/java/org/apache/ignite/internal/table/ItPartitionModificationCounterMetricsTest.java
@@ -18,8 +18,8 @@
 package org.apache.ignite.internal.table;
 
 import static org.apache.ignite.internal.TestWrappers.unwrapIgniteImpl;
+import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.DEFAULT_MIN_STALE_ROWS_COUNT;
 import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
-import static 
org.apache.ignite.internal.table.distributed.PartitionModificationCounterFactory.DEFAULT_MIN_STALE_ROWS_COUNT;
 import static 
org.apache.ignite.internal.table.distributed.PartitionModificationCounterMetricSource.METRIC_COUNTER;
 import static 
org.apache.ignite.internal.table.distributed.PartitionModificationCounterMetricSource.METRIC_LAST_MILESTONE_TIMESTAMP;
 import static 
org.apache.ignite.internal.table.distributed.PartitionModificationCounterMetricSource.METRIC_NEXT_MILESTONE;
diff --git 
a/modules/table/src/main/java/org/apache/ignite/internal/table/TableImpl.java 
b/modules/table/src/main/java/org/apache/ignite/internal/table/TableImpl.java
index 8e9908b84cf..c93b073ff6f 100644
--- 
a/modules/table/src/main/java/org/apache/ignite/internal/table/TableImpl.java
+++ 
b/modules/table/src/main/java/org/apache/ignite/internal/table/TableImpl.java
@@ -24,6 +24,7 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Supplier;
+import org.apache.ignite.internal.catalog.commands.CatalogUtils;
 import org.apache.ignite.internal.failure.FailureContext;
 import org.apache.ignite.internal.failure.FailureManager;
 import org.apache.ignite.internal.failure.FailureProcessor;
@@ -44,6 +45,7 @@ import 
org.apache.ignite.internal.table.distributed.IndexLocker;
 import org.apache.ignite.internal.table.distributed.PartitionSet;
 import org.apache.ignite.internal.table.distributed.TableIndexStoragesSupplier;
 import 
org.apache.ignite.internal.table.distributed.TableSchemaAwareIndexStorage;
+import 
org.apache.ignite.internal.table.distributed.TableStatsStalenessConfiguration;
 import org.apache.ignite.internal.table.distributed.schema.SchemaVersions;
 import org.apache.ignite.internal.table.metrics.TableMetricSource;
 import org.apache.ignite.internal.table.partition.HashPartitionManagerImpl;
@@ -55,6 +57,7 @@ import org.apache.ignite.table.RecordView;
 import org.apache.ignite.table.Tuple;
 import org.apache.ignite.table.mapper.Mapper;
 import org.apache.ignite.table.partition.PartitionManager;
+import org.jetbrains.annotations.Nullable;
 import org.jetbrains.annotations.TestOnly;
 
 /**
@@ -82,6 +85,8 @@ public class TableImpl implements TableViewInternal {
 
     private final int pkId;
 
+    private volatile TableStatsStalenessConfiguration configuration;
+
     /**
      * Constructor.
      *
@@ -100,7 +105,8 @@ public class TableImpl implements TableViewInternal {
             MarshallersProvider marshallers,
             IgniteSql sql,
             FailureProcessor failureProcessor,
-            int pkId
+            int pkId,
+            TableStatsStalenessConfiguration tableStatsStalenessConfiguration
     ) {
         this.tbl = tbl;
         this.lockManager = lockManager;
@@ -109,6 +115,7 @@ public class TableImpl implements TableViewInternal {
         this.sql = sql;
         this.failureProcessor = failureProcessor;
         this.pkId = pkId;
+        this.configuration = tableStatsStalenessConfiguration;
     }
 
     /**
@@ -137,7 +144,8 @@ public class TableImpl implements TableViewInternal {
                 new ReflectionMarshallersProvider(),
                 sql,
                 new FailureManager(new NoOpFailureHandler()),
-                pkId
+                pkId,
+                new 
TableStatsStalenessConfiguration(CatalogUtils.DEFAULT_STALE_ROWS_FRACTION, 
CatalogUtils.DEFAULT_MIN_STALE_ROWS_COUNT)
         );
 
         this.schemaReg = schemaReg;
@@ -316,4 +324,16 @@ public class TableImpl implements TableViewInternal {
     public TableMetricSource metrics() {
         return tbl.metrics();
     }
+
+    @Override
+    public void updateStalenessConfiguration(@Nullable Double 
staleRowsFraction, @Nullable Long minStaleRowsCount) {
+        TableStatsStalenessConfiguration configuration = this.configuration;
+
+        this.configuration = configuration.update(staleRowsFraction, 
minStaleRowsCount);
+    }
+
+    @Override
+    public TableStatsStalenessConfiguration stalenessConfiguration() {
+        return configuration;
+    }
 }
diff --git 
a/modules/table/src/main/java/org/apache/ignite/internal/table/TableViewInternal.java
 
b/modules/table/src/main/java/org/apache/ignite/internal/table/TableViewInternal.java
index 3dac8dd01d2..7ef9edccb2a 100644
--- 
a/modules/table/src/main/java/org/apache/ignite/internal/table/TableViewInternal.java
+++ 
b/modules/table/src/main/java/org/apache/ignite/internal/table/TableViewInternal.java
@@ -26,10 +26,12 @@ import 
org.apache.ignite.internal.storage.index.StorageSortedIndexDescriptor;
 import org.apache.ignite.internal.table.distributed.IndexLocker;
 import org.apache.ignite.internal.table.distributed.PartitionSet;
 import org.apache.ignite.internal.table.distributed.TableIndexStoragesSupplier;
+import 
org.apache.ignite.internal.table.distributed.TableStatsStalenessConfiguration;
 import org.apache.ignite.internal.table.metrics.TableMetricSource;
 import org.apache.ignite.table.Table;
 import org.apache.ignite.table.Tuple;
 import org.apache.ignite.table.mapper.Mapper;
+import org.jetbrains.annotations.Nullable;
 
 /** Internal table view interface. */
 public interface TableViewInternal extends Table {
@@ -135,4 +137,22 @@ public interface TableViewInternal extends Table {
      * @return Table metrics source.
      */
     TableMetricSource metrics();
+
+    /**
+     * Updates staleness configuration with provided parameters.
+     *
+     * <p>If parameter is {@code null}, then value from current configuration 
is used instead.
+     *
+     * @param staleRowsFraction A fraction of a partition to be modified 
before the data is considered to be "stale". Should be in
+     *         range [0, 1].
+     * @param minStaleRowsCount Minimal number of rows in partition to be 
modified before the data is considered to be "stale".
+     *         Should be non-negative.
+     */
+    void updateStalenessConfiguration(
+            @Nullable Double staleRowsFraction,
+            @Nullable Long minStaleRowsCount
+    );
+
+    /** Returns current staleness configuration. */
+    TableStatsStalenessConfiguration stalenessConfiguration();
 }
diff --git 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/PartitionModificationCounter.java
 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/PartitionModificationCounter.java
index 0c4311a6f63..e9bb24446fc 100644
--- 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/PartitionModificationCounter.java
+++ 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/PartitionModificationCounter.java
@@ -19,8 +19,9 @@ package org.apache.ignite.internal.table.distributed;
 
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicLong;
-import java.util.function.LongSupplier;
 import org.apache.ignite.internal.hlc.HybridTimestamp;
+import 
org.apache.ignite.internal.table.distributed.PartitionModificationCounterFactory.SizeSupplier;
+import 
org.apache.ignite.internal.table.distributed.PartitionModificationCounterFactory.StalenessConfigurationSupplier;
 
 /**
  * Keeps track of the number of modifications of a partition.
@@ -31,9 +32,8 @@ import org.apache.ignite.internal.hlc.HybridTimestamp;
  * <p>The timestamp value is used to determine the staleness of related SQL 
statistics.
  */
 public class PartitionModificationCounter {
-    private final LongSupplier partitionSizeSupplier;
-    private final double staleRowsFraction;
-    private final long minStaleRowsCount;
+    private final SizeSupplier partitionSizeSupplier;
+    private final StalenessConfigurationSupplier 
stalenessConfigurationSupplier;
 
     private final AtomicLong counter = new AtomicLong(0);
     private volatile long nextMilestone;
@@ -42,26 +42,23 @@ public class PartitionModificationCounter {
     /** Constructor. */
     public PartitionModificationCounter(
             HybridTimestamp initTimestamp,
-            LongSupplier partitionSizeSupplier,
-            double staleRowsFraction,
-            long minStaleRowsCount
+            SizeSupplier partitionSizeSupplier,
+            StalenessConfigurationSupplier stalenessConfigurationSupplier
     ) {
         Objects.requireNonNull(initTimestamp, "initTimestamp");
         Objects.requireNonNull(partitionSizeSupplier, "partitionSizeSupplier");
+        Objects.requireNonNull(stalenessConfigurationSupplier, 
"configurationProvider");
 
-        if (staleRowsFraction < 0 || staleRowsFraction > 1) {
-            throw new IllegalArgumentException("staleRowsFraction must be in 
[0, 1] range");
-        }
-
-        if (minStaleRowsCount < 0) {
-            throw new IllegalArgumentException("minStaleRowsCount must be 
non-negative");
-        }
-
-        this.staleRowsFraction = staleRowsFraction;
-        this.minStaleRowsCount = minStaleRowsCount;
         this.partitionSizeSupplier = partitionSizeSupplier;
+        this.stalenessConfigurationSupplier = stalenessConfigurationSupplier;
+
+        TableStatsStalenessConfiguration tableStatsStalenessConfiguration = 
stalenessConfigurationSupplier.get();
 
-        nextMilestone = 
computeNextMilestone(partitionSizeSupplier.getAsLong(), staleRowsFraction, 
minStaleRowsCount);
+        nextMilestone = computeNextMilestone(
+                partitionSizeSupplier.get(),
+                tableStatsStalenessConfiguration.staleRowsFraction(),
+                tableStatsStalenessConfiguration.minStaleRowsCount()
+        );
         lastMilestoneReachedTimestamp = initTimestamp;
     }
 
@@ -105,7 +102,14 @@ public class PartitionModificationCounter {
         long newCounter = counter.addAndGet(delta);
 
         if (newCounter >= nextMilestone) {
-            this.nextMilestone = newCounter + 
computeNextMilestone(partitionSizeSupplier.getAsLong(), staleRowsFraction, 
minStaleRowsCount);
+            long currentSize = partitionSizeSupplier.get();
+            TableStatsStalenessConfiguration tableStatsStalenessConfiguration 
= stalenessConfigurationSupplier.get();
+
+            this.nextMilestone = newCounter + computeNextMilestone(
+                    currentSize,
+                    tableStatsStalenessConfiguration.staleRowsFraction(),
+                    tableStatsStalenessConfiguration.minStaleRowsCount()
+            );
             this.lastMilestoneReachedTimestamp = commitTimestamp;
         }
     }
diff --git 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/PartitionModificationCounterFactory.java
 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/PartitionModificationCounterFactory.java
index 496a24077f6..9c4182c8a7f 100644
--- 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/PartitionModificationCounterFactory.java
+++ 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/PartitionModificationCounterFactory.java
@@ -17,7 +17,6 @@
 
 package org.apache.ignite.internal.table.distributed;
 
-import java.util.function.LongSupplier;
 import java.util.function.Supplier;
 import org.apache.ignite.internal.hlc.HybridTimestamp;
 
@@ -25,9 +24,6 @@ import org.apache.ignite.internal.hlc.HybridTimestamp;
  * Factory for producing {@link PartitionModificationCounter}.
  */
 public class PartitionModificationCounterFactory {
-    public static final long DEFAULT_MIN_STALE_ROWS_COUNT = 500L;
-
-    public static final double DEFAULT_STALE_ROWS_FRACTION = 0.2d;
 
     private final Supplier<HybridTimestamp> currentTimestampSupplier;
 
@@ -39,14 +35,29 @@ public class PartitionModificationCounterFactory {
      * Creates a new partition modification counter.
      *
      * @param partitionSizeSupplier Partition size supplier.
+     * @param stalenessConfigurationSupplier Partition size supplier.
      * @return New partition modification counter.
      */
-    public PartitionModificationCounter create(LongSupplier 
partitionSizeSupplier) {
+    public PartitionModificationCounter create(
+            SizeSupplier partitionSizeSupplier,
+            StalenessConfigurationSupplier stalenessConfigurationSupplier
+    ) {
         return new PartitionModificationCounter(
                 currentTimestampSupplier.get(),
                 partitionSizeSupplier,
-                DEFAULT_STALE_ROWS_FRACTION,
-                DEFAULT_MIN_STALE_ROWS_COUNT
+                stalenessConfigurationSupplier
         );
     }
+
+    /** An interface representing supplier of current size. */
+    @FunctionalInterface
+    public interface SizeSupplier {
+        long get();
+    }
+
+    /** An interface representing supplier of current staleness configuration. 
*/
+    @FunctionalInterface
+    public interface StalenessConfigurationSupplier {
+        TableStatsStalenessConfiguration get();
+    }
 }
diff --git 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableManager.java
 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableManager.java
index c03b57dd90c..eb7c7e92c97 100644
--- 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableManager.java
+++ 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableManager.java
@@ -97,7 +97,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
 import java.util.function.Function;
-import java.util.function.LongSupplier;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 import java.util.stream.IntStream;
@@ -107,6 +106,7 @@ import org.apache.ignite.internal.catalog.CatalogService;
 import org.apache.ignite.internal.catalog.descriptors.CatalogSchemaDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogZoneDescriptor;
+import 
org.apache.ignite.internal.catalog.events.AlterTablePropertiesEventParameters;
 import org.apache.ignite.internal.catalog.events.CatalogEvent;
 import org.apache.ignite.internal.catalog.events.CatalogEventParameters;
 import org.apache.ignite.internal.catalog.events.CreateTableEventParameters;
@@ -217,6 +217,7 @@ import org.apache.ignite.internal.table.LongPriorityQueue;
 import org.apache.ignite.internal.table.StreamerReceiverRunner;
 import org.apache.ignite.internal.table.TableImpl;
 import org.apache.ignite.internal.table.TableViewInternal;
+import 
org.apache.ignite.internal.table.distributed.PartitionModificationCounterFactory.SizeSupplier;
 import org.apache.ignite.internal.table.distributed.gc.GcUpdateHandler;
 import org.apache.ignite.internal.table.distributed.gc.MvGc;
 import org.apache.ignite.internal.table.distributed.index.IndexMetaStorage;
@@ -1193,6 +1194,8 @@ public class TableManager implements 
IgniteTablesInternal, IgniteComponent {
     private CompletableFuture<Boolean> onTableAlter(CatalogEventParameters 
parameters) {
         if (parameters instanceof RenameTableEventParameters) {
             return onTableRename((RenameTableEventParameters) 
parameters).thenApply(unused -> false);
+        } else if (parameters instanceof AlterTablePropertiesEventParameters) {
+            return 
onTablePropertiesChanged((AlterTablePropertiesEventParameters) 
parameters).thenApply(unused -> false);
         } else {
             return falseCompletedFuture();
         }
@@ -1218,6 +1221,23 @@ public class TableManager implements 
IgniteTablesInternal, IgniteComponent {
         }
     }
 
+    private CompletableFuture<?> 
onTablePropertiesChanged(AlterTablePropertiesEventParameters parameters) {
+        return inBusyLockAsync(busyLock, () -> tablesVv.update(
+                parameters.causalityToken(),
+                (ignore, e) -> {
+                    if (e != null) {
+                        return failedFuture(e);
+                    }
+
+                    TableViewInternal table = tables.get(parameters.tableId());
+
+                    
table.updateStalenessConfiguration(parameters.staleRowsFraction(), 
parameters.minStaleRowsCount());
+
+                    return nullCompletedFuture();
+                })
+        );
+    }
+
     private CompletableFuture<?> onTableRename(RenameTableEventParameters 
parameters) {
         return inBusyLockAsync(busyLock, () -> tablesVv.update(
                 parameters.causalityToken(),
@@ -1830,7 +1850,11 @@ public class TableManager implements 
IgniteTablesInternal, IgniteComponent {
                 marshallers,
                 sql.get(),
                 failureProcessor,
-                tableDescriptor.primaryKeyIndexId()
+                tableDescriptor.primaryKeyIndexId(),
+                new TableStatsStalenessConfiguration(
+                        tableDescriptor.properties().staleRowsFraction(),
+                        tableDescriptor.properties().minStaleRowsCount()
+                )
         );
     }
 
@@ -3159,8 +3183,10 @@ public class TableManager implements 
IgniteTablesInternal, IgniteComponent {
 
         GcUpdateHandler gcUpdateHandler = new 
GcUpdateHandler(partitionDataStorage, safeTimeTracker, indexUpdateHandler);
 
-        LongSupplier partSizeSupplier = () -> 
partitionDataStorage.getStorage().estimatedSize();
-        PartitionModificationCounter modificationCounter = 
partitionModificationCounterFactory.create(partSizeSupplier);
+        SizeSupplier partSizeSupplier = () -> 
partitionDataStorage.getStorage().estimatedSize();
+        PartitionModificationCounter modificationCounter = 
partitionModificationCounterFactory.create(
+                partSizeSupplier, table::stalenessConfiguration
+        );
         registerPartitionModificationCounterMetrics(table.tableId(), 
partitionId, modificationCounter);
 
         StorageUpdateHandler storageUpdateHandler = new StorageUpdateHandler(
diff --git 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableStatsStalenessConfiguration.java
 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableStatsStalenessConfiguration.java
new file mode 100644
index 00000000000..d967311399b
--- /dev/null
+++ 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableStatsStalenessConfiguration.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.table.distributed;
+
+import static java.util.Objects.requireNonNullElse;
+
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Container to store and atomically update pair of properties related to 
{@link PartitionModificationCounter} staleness.
+ *
+ * @see PartitionModificationCounter
+ */
+public class TableStatsStalenessConfiguration {
+    private final double staleRowsFraction;
+    private final long minStaleRowsCount;
+
+    /**
+     * Constructs the object.
+     *
+     * @param staleRowsFraction A fraction of a partition to be modified 
before the data is considered to be "stale". Should be in
+     *         range [0, 1].
+     * @param minStaleRowsCount Minimal number of rows in partition to be 
modified before the data is considered to be "stale".
+     *         Should be non-negative.
+     */
+    public TableStatsStalenessConfiguration(double staleRowsFraction, long 
minStaleRowsCount) {
+        if (staleRowsFraction < 0 || staleRowsFraction > 1) {
+            throw new IllegalArgumentException("staleRowsFraction must be in 
[0, 1] range");
+        }
+
+        if (minStaleRowsCount < 0) {
+            throw new IllegalArgumentException("minStaleRowsCount must be 
non-negative");
+        }
+
+        this.staleRowsFraction = staleRowsFraction;
+        this.minStaleRowsCount = minStaleRowsCount;
+    }
+
+    /** Returns fraction of a partition to be modified before the data is 
considered to be "stale". */
+    public double staleRowsFraction() {
+        return staleRowsFraction;
+    }
+
+    /** Returns minimal number of rows in partition to be modified before the 
data is considered to be "stale". */
+    public long minStaleRowsCount() {
+        return minStaleRowsCount;
+    }
+
+    /**
+     * Updates given configuration with provided parameters.
+     *
+     * <p>If parameter is {@code null}, then value from current configuration 
is used instead.
+     *
+     * @param staleRowsFraction A fraction of a partition to be modified 
before the data is considered to be "stale". Should be in
+     *         range [0, 1].
+     * @param minStaleRowsCount Minimal number of rows in partition to be 
modified before the data is considered to be "stale".
+     *         Should be non-negative.
+     * @return New object representing updated configuration.
+     */
+    public TableStatsStalenessConfiguration update(
+            @Nullable Double staleRowsFraction,
+            @Nullable Long minStaleRowsCount
+    ) {
+        if (staleRowsFraction == null && minStaleRowsCount == null) {
+            return this;
+        }
+
+        return new TableStatsStalenessConfiguration(
+                requireNonNullElse(staleRowsFraction, this.staleRowsFraction),
+                requireNonNullElse(minStaleRowsCount, this.minStaleRowsCount)
+        );
+    }
+}
diff --git 
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/PartitionModificationCounterTest.java
 
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/PartitionModificationCounterTest.java
index 40c0e31ef85..e2968577009 100644
--- 
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/PartitionModificationCounterTest.java
+++ 
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/PartitionModificationCounterTest.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.table.distributed;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 
+import org.apache.ignite.internal.catalog.commands.CatalogUtils;
 import org.apache.ignite.internal.hlc.HybridTimestamp;
 import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
 import org.apache.ignite.internal.testframework.IgniteTestUtils;
@@ -36,16 +37,20 @@ public class PartitionModificationCounterTest extends 
BaseIgniteAbstractTest {
     void initialValues() {
         // Empty table.
         {
-            PartitionModificationCounter counter = factory.create(() -> 0L);
+            PartitionModificationCounter counter = factory.create(
+                    () -> 0L, () -> new TableStatsStalenessConfiguration(0.5, 
200)
+            );
 
             assertThat(counter.value(), is(0L));
-            assertThat(counter.nextMilestone(), 
is(PartitionModificationCounterFactory.DEFAULT_MIN_STALE_ROWS_COUNT));
+            assertThat(counter.nextMilestone(), is(200L));
             assertThat(counter.lastMilestoneTimestamp().longValue(), is(1L));
         }
 
         // Table with 10k rows.
         {
-            PartitionModificationCounter counter = factory.create(() -> 
10_000L);
+            PartitionModificationCounter counter = factory.create(
+                    () -> 10_000L, () -> new 
TableStatsStalenessConfiguration(0.2, 200)
+            );
 
             assertThat(counter.value(), is(0L));
             assertThat(counter.nextMilestone(), is(2000L));
@@ -63,8 +68,13 @@ public class PartitionModificationCounterTest extends 
BaseIgniteAbstractTest {
     @Test
     void lastMilestoneTimestampUpdate() {
         int rowsCount = 10_000;
-        int threshold = (int) (rowsCount * 
PartitionModificationCounterFactory.DEFAULT_STALE_ROWS_FRACTION);
-        PartitionModificationCounter counter = factory.create(() -> rowsCount);
+        int threshold = (int) (rowsCount * 
CatalogUtils.DEFAULT_STALE_ROWS_FRACTION);
+        PartitionModificationCounter counter = factory.create(
+                () -> rowsCount,
+                () -> new TableStatsStalenessConfiguration(
+                        CatalogUtils.DEFAULT_STALE_ROWS_FRACTION, 
CatalogUtils.DEFAULT_MIN_STALE_ROWS_COUNT
+                )
+        );
 
         assertThat(counter.lastMilestoneTimestamp().longValue(), is(1L));
 
@@ -91,7 +101,9 @@ public class PartitionModificationCounterTest extends 
BaseIgniteAbstractTest {
     @Test
     @SuppressWarnings({"ThrowableNotThrown", 
"ResultOfObjectAllocationIgnored", "DataFlowIssue"})
     void invalidUpdateValues() {
-        PartitionModificationCounter counter = factory.create(() -> 0L);
+        PartitionModificationCounter counter = factory.create(
+                () -> 0L, () -> new TableStatsStalenessConfiguration(0.2, 500)
+        );
 
         IgniteTestUtils.assertThrows(NullPointerException.class,
                 () -> counter.updateValue(1, null), "commitTimestamp");
@@ -104,32 +116,24 @@ public class PartitionModificationCounterTest extends 
BaseIgniteAbstractTest {
 
         IgniteTestUtils.assertThrows(
                 NullPointerException.class,
-                () -> new PartitionModificationCounter(null, () -> 0L, 0.0d, 
0),
+                () -> new PartitionModificationCounter(null, () -> 0L, () -> 
new TableStatsStalenessConfiguration(
+                        0.2, 500
+                )),
                 "initTimestamp"
         );
 
         IgniteTestUtils.assertThrows(
                 NullPointerException.class,
-                () -> new 
PartitionModificationCounter(HybridTimestamp.MIN_VALUE, null, 0.0d, 0),
+                () -> new 
PartitionModificationCounter(HybridTimestamp.MIN_VALUE, null, () -> new 
TableStatsStalenessConfiguration(
+                        0.2, 500
+                )),
                 "partitionSizeSupplier"
         );
 
         IgniteTestUtils.assertThrows(
-                IllegalArgumentException.class,
-                () -> new 
PartitionModificationCounter(HybridTimestamp.MIN_VALUE, () -> 0L, 1.1d, 0),
-                "staleRowsFraction must be in [0, 1] range"
-        );
-
-        IgniteTestUtils.assertThrows(
-                IllegalArgumentException.class,
-                () -> new 
PartitionModificationCounter(HybridTimestamp.MIN_VALUE, () -> 0L, -0.1d, 0),
-                "staleRowsFraction must be in [0, 1] range"
-        );
-
-        IgniteTestUtils.assertThrows(
-                IllegalArgumentException.class,
-                () -> new 
PartitionModificationCounter(HybridTimestamp.MIN_VALUE, () -> 0L, -0.1d, -1),
-                "staleRowsFraction must be in [0, 1] range"
+                NullPointerException.class,
+                () -> new 
PartitionModificationCounter(HybridTimestamp.MIN_VALUE, () -> 0L, null),
+                "configurationProvider"
         );
     }
 }
diff --git 
a/modules/table/src/testFixtures/java/org/apache/ignite/internal/table/TableTestUtils.java
 
b/modules/table/src/testFixtures/java/org/apache/ignite/internal/table/TableTestUtils.java
index 988ab12a642..2f2aa1bdd07 100644
--- 
a/modules/table/src/testFixtures/java/org/apache/ignite/internal/table/TableTestUtils.java
+++ 
b/modules/table/src/testFixtures/java/org/apache/ignite/internal/table/TableTestUtils.java
@@ -25,7 +25,6 @@ import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
 import java.util.List;
-import java.util.function.LongSupplier;
 import org.apache.ignite.internal.catalog.CatalogCommand;
 import org.apache.ignite.internal.catalog.CatalogManager;
 import org.apache.ignite.internal.catalog.CatalogService;
@@ -47,6 +46,7 @@ import org.apache.ignite.internal.hlc.HybridTimestamp;
 import org.apache.ignite.internal.sql.SqlCommon;
 import 
org.apache.ignite.internal.table.distributed.PartitionModificationCounter;
 import 
org.apache.ignite.internal.table.distributed.PartitionModificationCounterFactory;
+import 
org.apache.ignite.internal.table.distributed.TableStatsStalenessConfiguration;
 import org.apache.ignite.sql.ColumnType;
 import org.jetbrains.annotations.Nullable;
 
@@ -66,13 +66,15 @@ public class TableTestUtils {
 
     /** No-op partition modification counter. */
     public static final PartitionModificationCounter 
NOOP_PARTITION_MODIFICATION_COUNTER =
-            new PartitionModificationCounter(HybridTimestamp.MIN_VALUE, () -> 
0, 0, 0);
+            new PartitionModificationCounter(HybridTimestamp.MIN_VALUE, () -> 
0, () -> new TableStatsStalenessConfiguration(0, 0));
 
     /** No-op partition modification counter factory. */
     public static PartitionModificationCounterFactory 
NOOP_PARTITION_MODIFICATION_COUNTER_FACTORY =
             new PartitionModificationCounterFactory(() -> 
HybridTimestamp.MIN_VALUE) {
                 @Override
-                public PartitionModificationCounter create(LongSupplier 
partitionSizeSupplier) {
+                public PartitionModificationCounter create(
+                        SizeSupplier partitionSizeSupplier, 
StalenessConfigurationSupplier configurationSupplier
+                ) {
                     return NOOP_PARTITION_MODIFICATION_COUNTER;
                 }
             };

Reply via email to