You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@ignite.apache.org by "Pavel Pereslegin (Jira)" <ji...@apache.org> on 2023/10/20 08:56:00 UTC

[jira] [Updated] (IGNITE-20498) Prevent potential catalog version order violations.

     [ https://issues.apache.org/jira/browse/IGNITE-20498?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Pavel Pereslegin updated IGNITE-20498:
--------------------------------------
    Description: 
Currently, catalog versions are stored in an ordered structure. The activation timestamp is used as a key, which depends on the configuration property "schemaSync.delayDuration".

Changes to "delayDuration" parameter in runtime may lead to a violation of the order in which catalog versions are stored. That is, the old version may be saved with a bigger timestamp than the newer one. 

As a result, the manager can return incorrect (older) version of the catalog using a timestamp.

reproducer:


{code:java}
public class CatalogDelayDurationChangeTest extends BaseIgniteAbstractTest {
    private static final String NODE_NAME = "node1";
    private static final String TABLE_NAME = "test1";
    private final HybridClock clock = new HybridClockImpl();
    private final AtomicLong delayFromConfig = new AtomicLong();

    @Test
    public void testChangeDelayDuration() {
        CatalogManager catalogMgr = createManager();

        // Prepare schema changes.
        ColumnParams column = ColumnParams.builder().name("ID").type(ColumnType.INT32).build();
        CatalogCommand cmd1 = BaseCatalogManagerTest.createTableCommand(TABLE_NAME, List.of(column), List.of("ID"), null);
        CatalogCommand cmd2 = BaseCatalogManagerTest.createTableCommand("test2", List.of(column), List.of("ID"), null);

        // Make first schema change with delay = 1000.
        delayFromConfig.set(1_000);
        CompletableFuture<Void> schemaChangeFuture0 = catalogMgr.execute(cmd1);

        // Make second schema change with delay = 1.
        delayFromConfig.set(1);
        CompletableFuture<Void> schemaChangeFuture1 = catalogMgr.execute(cmd2);

        assertThat(schemaChangeFuture0, willCompleteSuccessfully());
        assertThat(schemaChangeFuture1, willCompleteSuccessfully());

        // Make sure that we are getting the latest version of the schema using current timestamp.
        int latestVer = catalogMgr.latestCatalogVersion();
        int currentTsVer = catalogMgr.activeCatalogVersion(clock.now().longValue());
        assertThat(currentTsVer, equalTo(latestVer));
    }

    private CatalogManager createManager() {
        VaultManager vault = new VaultManager(new InMemoryVaultService());
        MetaStorageManager metastore = StandaloneMetaStorageManager.create(vault, new SimpleInMemoryKeyValueStorage(NODE_NAME));
        UpdateLog updateLog = spy(new UpdateLogImpl(metastore));
        ClockWaiter clockWaiter = spy(new ClockWaiter(NODE_NAME, clock));
        CatalogManager manager = new CatalogManagerImpl(updateLog, clockWaiter, delayFromConfig::get);

        vault.start();
        metastore.start();
        clockWaiter.start();
        manager.start();

        metastore.deployWatches().join();

        return manager;
    }
}
{code}


  was:
Currently, catalog versions are stored in an ordered structure. The activation timestamp is used as a key, which depends on the configuration property "schemaSync.delayDuration".

Changes to "delayDuration" parameter in runtime may lead to a violation of the order in which catalog versions are stored. That is, the old version may be saved with a larger timestamp than the newer one. 

As a result, the manager can return incorrect (older) version of the catalog using a timestamp.

reproducer:


{code:java}
public class CatalogDelayDurationChangeTest extends BaseIgniteAbstractTest {
    private static final String NODE_NAME = "node1";
    private static final String TABLE_NAME = "test1";
    private final HybridClock clock = new HybridClockImpl();
    private final AtomicLong delayFromConfig = new AtomicLong();

    @Test
    public void testChangeDelayDuration() {
        CatalogManager catalogMgr = createManager();

        // Prepare schema changes.
        ColumnParams column = ColumnParams.builder().name("ID").type(ColumnType.INT32).build();
        CatalogCommand cmd1 = BaseCatalogManagerTest.createTableCommand(TABLE_NAME, List.of(column), List.of("ID"), null);
        CatalogCommand cmd2 = BaseCatalogManagerTest.createTableCommand("test2", List.of(column), List.of("ID"), null);

        // Make first schema change with delay = 1000.
        delayFromConfig.set(1_000);
        CompletableFuture<Void> schemaChangeFuture0 = catalogMgr.execute(cmd1);

        // Make second schema change with delay = 1.
        delayFromConfig.set(1);
        CompletableFuture<Void> schemaChangeFuture1 = catalogMgr.execute(cmd2);

        assertThat(schemaChangeFuture0, willCompleteSuccessfully());
        assertThat(schemaChangeFuture1, willCompleteSuccessfully());

        // Make sure that we are getting the latest version of the schema using current timestamp.
        int latestVer = catalogMgr.latestCatalogVersion();
        int currentTsVer = catalogMgr.activeCatalogVersion(clock.now().longValue());
        assertThat(currentTsVer, equalTo(latestVer));
    }

    private CatalogManager createManager() {
        VaultManager vault = new VaultManager(new InMemoryVaultService());
        MetaStorageManager metastore = StandaloneMetaStorageManager.create(vault, new SimpleInMemoryKeyValueStorage(NODE_NAME));
        UpdateLog updateLog = spy(new UpdateLogImpl(metastore));
        ClockWaiter clockWaiter = spy(new ClockWaiter(NODE_NAME, clock));
        CatalogManager manager = new CatalogManagerImpl(updateLog, clockWaiter, delayFromConfig::get);

        vault.start();
        metastore.start();
        clockWaiter.start();
        manager.start();

        metastore.deployWatches().join();

        return manager;
    }
}
{code}



> Prevent potential catalog version order violations.
> ---------------------------------------------------
>
>                 Key: IGNITE-20498
>                 URL: https://issues.apache.org/jira/browse/IGNITE-20498
>             Project: Ignite
>          Issue Type: Bug
>            Reporter: Pavel Pereslegin
>            Priority: Major
>              Labels: ignite-3
>
> Currently, catalog versions are stored in an ordered structure. The activation timestamp is used as a key, which depends on the configuration property "schemaSync.delayDuration".
> Changes to "delayDuration" parameter in runtime may lead to a violation of the order in which catalog versions are stored. That is, the old version may be saved with a bigger timestamp than the newer one. 
> As a result, the manager can return incorrect (older) version of the catalog using a timestamp.
> reproducer:
> {code:java}
> public class CatalogDelayDurationChangeTest extends BaseIgniteAbstractTest {
>     private static final String NODE_NAME = "node1";
>     private static final String TABLE_NAME = "test1";
>     private final HybridClock clock = new HybridClockImpl();
>     private final AtomicLong delayFromConfig = new AtomicLong();
>     @Test
>     public void testChangeDelayDuration() {
>         CatalogManager catalogMgr = createManager();
>         // Prepare schema changes.
>         ColumnParams column = ColumnParams.builder().name("ID").type(ColumnType.INT32).build();
>         CatalogCommand cmd1 = BaseCatalogManagerTest.createTableCommand(TABLE_NAME, List.of(column), List.of("ID"), null);
>         CatalogCommand cmd2 = BaseCatalogManagerTest.createTableCommand("test2", List.of(column), List.of("ID"), null);
>         // Make first schema change with delay = 1000.
>         delayFromConfig.set(1_000);
>         CompletableFuture<Void> schemaChangeFuture0 = catalogMgr.execute(cmd1);
>         // Make second schema change with delay = 1.
>         delayFromConfig.set(1);
>         CompletableFuture<Void> schemaChangeFuture1 = catalogMgr.execute(cmd2);
>         assertThat(schemaChangeFuture0, willCompleteSuccessfully());
>         assertThat(schemaChangeFuture1, willCompleteSuccessfully());
>         // Make sure that we are getting the latest version of the schema using current timestamp.
>         int latestVer = catalogMgr.latestCatalogVersion();
>         int currentTsVer = catalogMgr.activeCatalogVersion(clock.now().longValue());
>         assertThat(currentTsVer, equalTo(latestVer));
>     }
>     private CatalogManager createManager() {
>         VaultManager vault = new VaultManager(new InMemoryVaultService());
>         MetaStorageManager metastore = StandaloneMetaStorageManager.create(vault, new SimpleInMemoryKeyValueStorage(NODE_NAME));
>         UpdateLog updateLog = spy(new UpdateLogImpl(metastore));
>         ClockWaiter clockWaiter = spy(new ClockWaiter(NODE_NAME, clock));
>         CatalogManager manager = new CatalogManagerImpl(updateLog, clockWaiter, delayFromConfig::get);
>         vault.start();
>         metastore.start();
>         clockWaiter.start();
>         manager.start();
>         metastore.deployWatches().join();
>         return manager;
>     }
> }
> {code}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)