private void onFindPrimaryShardSuccess(
      PrimaryShardInfo primaryShardInfo,
      TransactionProxy parent,
      String shardName,
      TransactionContextWrapper transactionContextWrapper) {
    if (LOG.isDebugEnabled()) {
      LOG.debug(
          "Tx {}: Found primary {} for shard {}",
          parent.getIdentifier(),
          primaryShardInfo.getPrimaryShardActor(),
          shardName);
    }

    updateShardInfo(shardName, primaryShardInfo);

    try {
      TransactionContext localContext = maybeCreateLocalTransactionContext(parent, shardName);
      if (localContext != null) {
        transactionContextWrapper.executePriorTransactionOperations(localContext);
      } else {
        RemoteTransactionContextSupport remote =
            new RemoteTransactionContextSupport(transactionContextWrapper, parent, shardName);
        remote.setPrimaryShard(
            primaryShardInfo.getPrimaryShardActor(), primaryShardInfo.getPrimaryShardVersion());
      }
    } finally {
      onTransactionContextCreated(parent.getIdentifier());
    }
  }
  @Test
  public void testFindPrimaryShardAsyncRemotePrimaryFound() throws Exception {

    TestActorRef<MessageCollectorActor> shardManager =
        TestActorRef.create(getSystem(), Props.create(MessageCollectorActor.class));

    DatastoreContext dataStoreContext =
        DatastoreContext.newBuilder()
            .dataStoreType("config")
            .shardLeaderElectionTimeout(100, TimeUnit.MILLISECONDS)
            .build();

    final String expPrimaryPath = "akka://test-system/find-primary-shard";
    final short expPrimaryVersion = DataStoreVersions.CURRENT_VERSION;
    ActorContext actorContext =
        new ActorContext(
            getSystem(),
            shardManager,
            mock(ClusterWrapper.class),
            mock(Configuration.class),
            dataStoreContext,
            new PrimaryShardInfoFutureCache()) {
          @Override
          protected Future<Object> doAsk(ActorRef actorRef, Object message, Timeout timeout) {
            return Futures.successful(
                (Object) new RemotePrimaryShardFound(expPrimaryPath, expPrimaryVersion));
          }
        };

    Future<PrimaryShardInfo> foobar = actorContext.findPrimaryShardAsync("foobar");
    PrimaryShardInfo actual = Await.result(foobar, Duration.apply(5000, TimeUnit.MILLISECONDS));

    assertNotNull(actual);
    assertEquals("LocalShardDataTree present", false, actual.getLocalShardDataTree().isPresent());
    assertTrue(
        "Unexpected PrimaryShardActor path " + actual.getPrimaryShardActor().path(),
        expPrimaryPath.endsWith(actual.getPrimaryShardActor().pathString()));
    assertEquals("getPrimaryShardVersion", expPrimaryVersion, actual.getPrimaryShardVersion());

    Future<PrimaryShardInfo> cached =
        actorContext.getPrimaryShardInfoCache().getIfPresent("foobar");

    PrimaryShardInfo cachedInfo =
        Await.result(cached, FiniteDuration.apply(1, TimeUnit.MILLISECONDS));

    assertEquals(cachedInfo, actual);

    actorContext.getPrimaryShardInfoCache().remove("foobar");

    cached = actorContext.getPrimaryShardInfoCache().getIfPresent("foobar");

    assertNull(cached);
  }
  private void updateShardInfo(final String shardName, final PrimaryShardInfo primaryShardInfo) {
    final Optional<DataTree> maybeDataTree = primaryShardInfo.getLocalShardDataTree();
    if (maybeDataTree.isPresent()) {
      if (!knownLocal.containsKey(shardName)) {
        LOG.debug("Shard {} resolved to local data tree - adding local factory", shardName);

        F factory =
            factoryForShard(
                shardName, primaryShardInfo.getPrimaryShardActor(), maybeDataTree.get());
        knownLocal.putIfAbsent(shardName, factory);
      }
    } else if (knownLocal.containsKey(shardName)) {
      LOG.debug("Shard {} invalidating local data tree", shardName);

      knownLocal.remove(shardName);
    }
  }