+ final Optional<NormalizedNode<?, ?>> optional = txChain.newReadOnlyTransaction()
+ .read(CarsModel.CAR_LIST_PATH).get(5, TimeUnit.SECONDS);
+ assertEquals("isPresent", true, optional.isPresent());
+ assertEquals("# cars", numCars, ((Collection<?>) optional.get().getValue()).size());
+ }
+ }
+ };
+ }
+
+ @SuppressWarnings("checkstyle:IllegalCatch")
+ private void testTransactionWritesWithShardNotInitiallyReady(final String testName, final boolean writeOnly)
+ throws Exception {
+ new IntegrationTestKit(getSystem(), datastoreContextBuilder) {
+ {
+ final String shardName = "test-1";
+
+ // Setup the InMemoryJournal to block shard recovery to ensure
+ // the shard isn't
+ // initialized until we create and submit the write the Tx.
+ final String persistentID = String.format("member-1-shard-%s-%s", shardName, testName);
+ final CountDownLatch blockRecoveryLatch = new CountDownLatch(1);
+ InMemoryJournal.addBlockReadMessagesLatch(persistentID, blockRecoveryLatch);
+
+ try (AbstractDataStore dataStore = setupAbstractDataStore(
+ testParameter, testName, false, shardName)) {
+
+ // Create the write Tx
+ final DOMStoreWriteTransaction writeTx = writeOnly ? dataStore.newWriteOnlyTransaction()
+ : dataStore.newReadWriteTransaction();
+ assertNotNull("newReadWriteTransaction returned null", writeTx);
+
+ // Do some modification operations and ready the Tx on a
+ // separate thread.
+ final YangInstanceIdentifier listEntryPath = YangInstanceIdentifier
+ .builder(TestModel.OUTER_LIST_PATH)
+ .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).build();
+
+ final AtomicReference<DOMStoreThreePhaseCommitCohort> txCohort = new AtomicReference<>();
+ final AtomicReference<Exception> caughtEx = new AtomicReference<>();
+ final CountDownLatch txReady = new CountDownLatch(1);
+ final Thread txThread = new Thread() {
+ @Override
+ public void run() {
+ try {
+ writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+ writeTx.merge(TestModel.OUTER_LIST_PATH,
+ ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build());
+
+ writeTx.write(listEntryPath,
+ ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1));
+
+ writeTx.delete(listEntryPath);
+
+ txCohort.set(writeTx.ready());
+ } catch (Exception e) {
+ caughtEx.set(e);
+ } finally {
+ txReady.countDown();
+ }
+ }
+ };
+
+ txThread.start();
+
+ // Wait for the Tx operations to complete.
+ final boolean done = Uninterruptibles.awaitUninterruptibly(txReady, 5, TimeUnit.SECONDS);
+ if (caughtEx.get() != null) {
+ throw caughtEx.get();
+ }
+
+ assertEquals("Tx ready", true, done);
+
+ // At this point the Tx operations should be waiting for the
+ // shard to initialize so
+ // trigger the latch to let the shard recovery to continue.
+ blockRecoveryLatch.countDown();
+
+ // Wait for the Tx commit to complete.
+ doCommit(txCohort.get());
+
+ // Verify the data in the store
+ final DOMStoreReadTransaction readTx = dataStore.newReadOnlyTransaction();
+
+ Optional<NormalizedNode<?, ?>> optional = readTx.read(TestModel.TEST_PATH).get(5, TimeUnit.SECONDS);
+ assertEquals("isPresent", true, optional.isPresent());
+
+ optional = readTx.read(TestModel.OUTER_LIST_PATH).get(5, TimeUnit.SECONDS);
+ assertEquals("isPresent", true, optional.isPresent());
+
+ optional = readTx.read(listEntryPath).get(5, TimeUnit.SECONDS);
+ assertEquals("isPresent", false, optional.isPresent());
+ }
+ }
+ };
+ }
+
+ @Test
+ public void testWriteOnlyTransactionWithShardNotInitiallyReady() throws Exception {
+ datastoreContextBuilder.writeOnlyTransactionOptimizationsEnabled(true);
+ testTransactionWritesWithShardNotInitiallyReady("testWriteOnlyTransactionWithShardNotInitiallyReady", true);
+ }
+
+ @Test
+ public void testReadWriteTransactionWithShardNotInitiallyReady() throws Exception {
+ testTransactionWritesWithShardNotInitiallyReady("testReadWriteTransactionWithShardNotInitiallyReady", false);
+ }
+
+ @Test
+ @SuppressWarnings("checkstyle:IllegalCatch")
+ public void testTransactionReadsWithShardNotInitiallyReady() throws Exception {
+ new IntegrationTestKit(getSystem(), datastoreContextBuilder) {
+ {
+ final String testName = "testTransactionReadsWithShardNotInitiallyReady";
+ final String shardName = "test-1";
+
+ // Setup the InMemoryJournal to block shard recovery to ensure
+ // the shard isn't
+ // initialized until we create the Tx.
+ final String persistentID = String.format("member-1-shard-%s-%s", shardName, testName);
+ final CountDownLatch blockRecoveryLatch = new CountDownLatch(1);
+ InMemoryJournal.addBlockReadMessagesLatch(persistentID, blockRecoveryLatch);
+
+ try (AbstractDataStore dataStore = setupAbstractDataStore(
+ testParameter, testName, false, shardName)) {
+
+ // Create the read-write Tx
+ final DOMStoreReadWriteTransaction readWriteTx = dataStore.newReadWriteTransaction();
+ assertNotNull("newReadWriteTransaction returned null", readWriteTx);
+
+ // Do some reads on the Tx on a separate thread.
+ final AtomicReference<CheckedFuture<Boolean, ReadFailedException>> txExistsFuture =
+ new AtomicReference<>();
+ final AtomicReference<CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException>>
+ txReadFuture = new AtomicReference<>();
+ final AtomicReference<Exception> caughtEx = new AtomicReference<>();
+ final CountDownLatch txReadsDone = new CountDownLatch(1);
+ final Thread txThread = new Thread() {
+ @Override
+ public void run() {
+ try {
+ readWriteTx.write(TestModel.TEST_PATH,
+ ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+ txExistsFuture.set(readWriteTx.exists(TestModel.TEST_PATH));
+
+ txReadFuture.set(readWriteTx.read(TestModel.TEST_PATH));
+ } catch (Exception e) {
+ caughtEx.set(e);
+ } finally {
+ txReadsDone.countDown();
+ }
+ }
+ };
+
+ txThread.start();
+
+ // Wait for the Tx operations to complete.
+ boolean done = Uninterruptibles.awaitUninterruptibly(txReadsDone, 5, TimeUnit.SECONDS);
+ if (caughtEx.get() != null) {
+ throw caughtEx.get();
+ }
+
+ assertEquals("Tx reads done", true, done);
+
+ // At this point the Tx operations should be waiting for the
+ // shard to initialize so
+ // trigger the latch to let the shard recovery to continue.
+ blockRecoveryLatch.countDown();
+
+ // Wait for the reads to complete and verify.
+ assertEquals("exists", true, txExistsFuture.get().checkedGet(5, TimeUnit.SECONDS));
+ assertEquals("read", true, txReadFuture.get().checkedGet(5, TimeUnit.SECONDS).isPresent());
+
+ readWriteTx.close();
+ }
+ }
+ };
+ }