+
+ @Override
+ LeaderFrontendState toLeaderState(final Shard shard) {
+ // Note: we have to make sure to *copy* all current state and not leak any views, otherwise leader/follower
+ // interactions would get intertwined leading to inconsistencies.
+ final Map<LocalHistoryIdentifier, LocalFrontendHistory> histories = new HashMap<>();
+ for (FrontendHistoryMetadataBuilder e : currentHistories.values()) {
+ if (e.getIdentifier().getHistoryId() != 0) {
+ final AbstractFrontendHistory state = e.toLeaderState(shard);
+ verify(state instanceof LocalFrontendHistory, "Unexpected state %s", state);
+ histories.put(e.getIdentifier(), (LocalFrontendHistory) state);
+ }
+ }
+
+ final AbstractFrontendHistory singleHistory;
+ final FrontendHistoryMetadataBuilder singleHistoryMeta = currentHistories.get(
+ new LocalHistoryIdentifier(getIdentifier(), 0));
+ if (singleHistoryMeta == null) {
+ final ShardDataTree tree = shard.getDataStore();
+ singleHistory = StandaloneFrontendHistory.create(shard.persistenceId(), getIdentifier(), tree);
+ } else {
+ singleHistory = singleHistoryMeta.toLeaderState(shard);
+ }
+
+ return new LeaderFrontendState.Enabled(shard.persistenceId(), getIdentifier(), shard.getDataStore(),
+ purgedHistories.copy(), singleHistory, histories);
+ }
+
+ @Override
+ ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+ return super.addToStringAttributes(helper).add("current", currentHistories).add("purged", purgedHistories);
+ }
+
+ private FrontendHistoryMetadataBuilder getHistory(final TransactionIdentifier txId) {
+ LocalHistoryIdentifier historyId = txId.getHistoryId();
+ if (historyId.getHistoryId() == 0 && historyId.getCookie() != 0) {
+ // We are pre-creating the history for free-standing transactions with a zero cookie, hence our lookup
+ // needs to account for that.
+ LOG.debug("{}: looking up {} instead of {}", shardName(), standaloneId, historyId);
+ historyId = standaloneId;
+ }
+
+ return currentHistories.get(historyId);
+ }
+
+ private LocalHistoryIdentifier standaloneHistoryId() {
+ return new LocalHistoryIdentifier(getIdentifier(), 0);
+ }
+ }
+
+ private static final Logger LOG = LoggerFactory.getLogger(FrontendClientMetadataBuilder.class);
+
+ private final ClientIdentifier identifier;
+ private final String shardName;
+
+ FrontendClientMetadataBuilder(final String shardName, final ClientIdentifier identifier) {
+ this.shardName = requireNonNull(shardName);
+ this.identifier = requireNonNull(identifier);
+ }
+
+ static FrontendClientMetadataBuilder of(final String shardName, final FrontendClientMetadata meta) {
+ final Collection<FrontendHistoryMetadata> current = meta.getCurrentHistories();
+ final RangeSet<UnsignedLong> purged = meta.getPurgedHistories();
+
+ // Completely empty histories imply disabled state, as otherwise we'd have a record of the single history --
+ // either purged or active
+ return current.isEmpty() && purged.isEmpty() ? new Disabled(shardName, meta.getIdentifier())
+ : new Enabled(shardName, meta);