perf improvements

This commit is contained in:
Benedict Elliott Smith 2024-02-28 15:50:04 +00:00 committed by David Capwell
parent 599cd59736
commit 7dadc080cb
40 changed files with 654 additions and 320 deletions

@ -1 +1 @@
Subproject commit ef36616441bd4ff4fec5379d986c75ad5a62ff7d Subproject commit 3562bb3c9ce4e9eecdf65e236e968ef3ee9e0a86

View File

@ -47,9 +47,9 @@ public class AccordSpec
public volatile DurationSpec fast_path_update_delay = new DurationSpec.IntSecondsBound(5); public volatile DurationSpec fast_path_update_delay = new DurationSpec.IntSecondsBound(5);
public volatile DurationSpec schedule_durability_frequency = new DurationSpec.IntSecondsBound(15); public volatile DurationSpec schedule_durability_frequency = new DurationSpec.IntSecondsBound(5);
public volatile DurationSpec durability_txnid_lag = new DurationSpec.IntSecondsBound(5); public volatile DurationSpec durability_txnid_lag = new DurationSpec.IntSecondsBound(5);
public volatile DurationSpec shard_durability_cycle = new DurationSpec.IntMinutesBound(2); public volatile DurationSpec shard_durability_cycle = new DurationSpec.IntMinutesBound(1);
public volatile DurationSpec global_durability_cycle = new DurationSpec.IntMinutesBound(10); public volatile DurationSpec global_durability_cycle = new DurationSpec.IntMinutesBound(10);
public enum TransactionalRangeMigration public enum TransactionalRangeMigration

View File

@ -500,6 +500,7 @@ public class Config
public volatile int counter_cache_keys_to_save = Integer.MAX_VALUE; public volatile int counter_cache_keys_to_save = Integer.MAX_VALUE;
public DataStorageSpec.LongMebibytesBound paxos_cache_size = null; public DataStorageSpec.LongMebibytesBound paxos_cache_size = null;
public DataStorageSpec.LongMebibytesBound accord_cache_size = null;
public DataStorageSpec.LongMebibytesBound consensus_migration_cache_size = null; public DataStorageSpec.LongMebibytesBound consensus_migration_cache_size = null;

View File

@ -227,6 +227,7 @@ public class DatabaseDescriptor
private static long keyCacheSizeInMiB; private static long keyCacheSizeInMiB;
private static long paxosCacheSizeInMiB; private static long paxosCacheSizeInMiB;
private static long accordCacheSizeInMiB;
private static long consensusMigrationCacheSizeInMiB; private static long consensusMigrationCacheSizeInMiB;
private static long counterCacheSizeInMiB; private static long counterCacheSizeInMiB;
private static long indexSummaryCapacityInMiB; private static long indexSummaryCapacityInMiB;
@ -963,6 +964,22 @@ public class DatabaseDescriptor
+ conf.paxos_cache_size + "', supported values are <integer> >= 0.", false); + conf.paxos_cache_size + "', supported values are <integer> >= 0.", false);
} }
try
{
// if paxosCacheSizeInMiB option was set to "auto" then size of the cache should be "max(10% of Heap (in MB), 1MB)
accordCacheSizeInMiB = (conf.accord_cache_size == null)
? Math.max(1, (int) ((Runtime.getRuntime().totalMemory() * 0.10) / 1024 / 1024))
: conf.accord_cache_size.toMebibytes();
if (accordCacheSizeInMiB < 0)
throw new NumberFormatException(); // to escape duplicating error message
}
catch (NumberFormatException e)
{
throw new ConfigurationException("paxos_cache_size option was set incorrectly to '"
+ conf.paxos_cache_size + "', supported values are <integer> >= 0.", false);
}
try try
{ {
// if consensusMigrationCacheSizeInMiB option was set to "auto" then size of the cache should be "min(1% of Heap (in MB), 50MB) // if consensusMigrationCacheSizeInMiB option was set to "auto" then size of the cache should be "min(1% of Heap (in MB), 50MB)
@ -4266,6 +4283,11 @@ public class DatabaseDescriptor
return paxosCacheSizeInMiB; return paxosCacheSizeInMiB;
} }
public static long getAccordCacheSizeInMiB()
{
return accordCacheSizeInMiB;
}
public static long getConsensusMigrationCacheSizeInMiB() public static long getConsensusMigrationCacheSizeInMiB()
{ {
return consensusMigrationCacheSizeInMiB; return consensusMigrationCacheSizeInMiB;

View File

@ -978,11 +978,7 @@ public class CompactionIterator extends CompactionInfo.Holder implements Unfilte
if (redundantBeforeEntry == null) if (redundantBeforeEntry == null)
return row; return row;
TxnId redundantBeforeTxnId = redundantBeforeEntry.shardRedundantBefore(); return CommandsForKeysAccessor.withoutRedundantCommands(partitionKey, row, redundantBeforeEntry);
if (redundantBeforeTxnId.equals(TxnId.NONE))
return row;
return CommandsForKeysAccessor.withoutRedundantCommands(partitionKey, row, redundantBeforeTxnId);
} }
@Override @Override

View File

@ -268,7 +268,7 @@ public enum Verb
PAXOS2_PREPARE_REQ (40, P2, writeTimeout, MUTATION, () -> PaxosPrepare.requestSerializer, () -> PaxosPrepare.requestHandler, PAXOS2_PREPARE_RSP ), PAXOS2_PREPARE_REQ (40, P2, writeTimeout, MUTATION, () -> PaxosPrepare.requestSerializer, () -> PaxosPrepare.requestHandler, PAXOS2_PREPARE_RSP ),
PAXOS2_PREPARE_REFRESH_RSP (51, P2, writeTimeout, REQUEST_RESPONSE, () -> PaxosPrepareRefresh.responseSerializer, RESPONSE_HANDLER ), PAXOS2_PREPARE_REFRESH_RSP (51, P2, writeTimeout, REQUEST_RESPONSE, () -> PaxosPrepareRefresh.responseSerializer, RESPONSE_HANDLER ),
PAXOS2_PREPARE_REFRESH_REQ (41, P2, writeTimeout, MUTATION, () -> PaxosPrepareRefresh.requestSerializer, () -> PaxosPrepareRefresh.requestHandler, PAXOS2_PREPARE_REFRESH_RSP ), PAXOS2_PREPARE_REFRESH_REQ (41, P2, writeTimeout, MUTATION, () -> PaxosPrepareRefresh.requestSerializer, () -> PaxosPrepareRefresh.requestHandler, PAXOS2_PREPARE_REFRESH_RSP ),
PAXOS2_PROPOSE_RSP (52, P2, writeTimeout, REQUEST_RESPONSE, () -> PaxosPropose.ACCEPT_RESULT_SERIALIZER, RESPONSE_HANDLER ), PAXOS2_PROPOSE_RSP (52, P2, writeTimeout, REQUEST_RESPONSE, () -> PaxosPropose.ACCEPT_RESULT_SERIALIZER, RESPONSE_HANDLER ),
PAXOS2_PROPOSE_REQ (42, P2, writeTimeout, MUTATION, () -> PaxosPropose.requestSerializer, () -> PaxosPropose.requestHandler, PAXOS2_PROPOSE_RSP ), PAXOS2_PROPOSE_REQ (42, P2, writeTimeout, MUTATION, () -> PaxosPropose.requestSerializer, () -> PaxosPropose.requestHandler, PAXOS2_PROPOSE_RSP ),
PAXOS2_COMMIT_AND_PREPARE_RSP (53, P2, writeTimeout, REQUEST_RESPONSE, () -> PaxosPrepare.responseSerializer, RESPONSE_HANDLER ), PAXOS2_COMMIT_AND_PREPARE_RSP (53, P2, writeTimeout, REQUEST_RESPONSE, () -> PaxosPrepare.responseSerializer, RESPONSE_HANDLER ),
PAXOS2_COMMIT_AND_PREPARE_REQ (43, P2, writeTimeout, MUTATION, () -> PaxosCommitAndPrepare.requestSerializer, () -> PaxosCommitAndPrepare.requestHandler, PAXOS2_COMMIT_AND_PREPARE_RSP ), PAXOS2_COMMIT_AND_PREPARE_REQ (43, P2, writeTimeout, MUTATION, () -> PaxosCommitAndPrepare.requestSerializer, () -> PaxosCommitAndPrepare.requestHandler, PAXOS2_COMMIT_AND_PREPARE_RSP ),
@ -307,41 +307,41 @@ public enum Verb
DATA_MOVEMENT_EXECUTED_REQ (817, P1, rpcTimeout, MISC, () -> DataMovement.Status.serializer, () -> DataMovements.instance, DATA_MOVEMENT_EXECUTED_RSP ), DATA_MOVEMENT_EXECUTED_REQ (817, P1, rpcTimeout, MISC, () -> DataMovement.Status.serializer, () -> DataMovements.instance, DATA_MOVEMENT_EXECUTED_RSP ),
// accord // accord
ACCORD_SIMPLE_RSP (119, P2, writeTimeout, REQUEST_RESPONSE, () -> EnumSerializer.simpleReply, RESPONSE_HANDLER ), ACCORD_SIMPLE_RSP (119, P2, writeTimeout, IMMEDIATE, () -> EnumSerializer.simpleReply, RESPONSE_HANDLER ),
ACCORD_PRE_ACCEPT_RSP (120, P2, writeTimeout, REQUEST_RESPONSE, () -> PreacceptSerializers.reply, RESPONSE_HANDLER ), ACCORD_PRE_ACCEPT_RSP (120, P2, writeTimeout, IMMEDIATE, () -> PreacceptSerializers.reply, RESPONSE_HANDLER ),
ACCORD_PRE_ACCEPT_REQ (121, P2, writeTimeout, IMMEDIATE, () -> PreacceptSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_PRE_ACCEPT_RSP ), ACCORD_PRE_ACCEPT_REQ (121, P2, writeTimeout, IMMEDIATE, () -> PreacceptSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_PRE_ACCEPT_RSP ),
ACCORD_ACCEPT_RSP (122, P2, writeTimeout, REQUEST_RESPONSE, () -> AcceptSerializers.reply, RESPONSE_HANDLER ), ACCORD_ACCEPT_RSP (122, P2, writeTimeout, IMMEDIATE, () -> AcceptSerializers.reply, RESPONSE_HANDLER ),
ACCORD_ACCEPT_REQ (123, P2, writeTimeout, IMMEDIATE, () -> AcceptSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_ACCEPT_RSP ), ACCORD_ACCEPT_REQ (123, P2, writeTimeout, IMMEDIATE, () -> AcceptSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_ACCEPT_RSP ),
ACCORD_ACCEPT_INVALIDATE_REQ (124, P2, writeTimeout, IMMEDIATE, () -> AcceptSerializers.invalidate, AccordService::verbHandlerOrNoop, ACCORD_ACCEPT_RSP ), ACCORD_ACCEPT_INVALIDATE_REQ (124, P2, writeTimeout, IMMEDIATE, () -> AcceptSerializers.invalidate, AccordService::verbHandlerOrNoop, ACCORD_ACCEPT_RSP ),
ACCORD_READ_RSP (125, P2, writeTimeout, REQUEST_RESPONSE, () -> ReadDataSerializers.reply, RESPONSE_HANDLER ), ACCORD_READ_RSP (125, P2, writeTimeout, IMMEDIATE, () -> ReadDataSerializers.reply, RESPONSE_HANDLER ),
ACCORD_READ_REQ (126, P2, writeTimeout, IMMEDIATE, () -> ReadDataSerializers.readData, AccordService::verbHandlerOrNoop, ACCORD_READ_RSP ), ACCORD_READ_REQ (126, P2, writeTimeout, IMMEDIATE, () -> ReadDataSerializers.readData, AccordService::verbHandlerOrNoop, ACCORD_READ_RSP ),
ACCORD_COMMIT_REQ (127, P2, writeTimeout, IMMEDIATE, () -> CommitSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_READ_RSP ), ACCORD_COMMIT_REQ (127, P2, writeTimeout, IMMEDIATE, () -> CommitSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_READ_RSP ),
ACCORD_COMMIT_INVALIDATE_REQ (128, P2, writeTimeout, IMMEDIATE, () -> CommitSerializers.invalidate, AccordService::verbHandlerOrNoop ), ACCORD_COMMIT_INVALIDATE_REQ (128, P2, writeTimeout, IMMEDIATE, () -> CommitSerializers.invalidate, AccordService::verbHandlerOrNoop ),
ACCORD_APPLY_RSP (129, P2, writeTimeout, REQUEST_RESPONSE, () -> ApplySerializers.reply, RESPONSE_HANDLER ), ACCORD_APPLY_RSP (129, P2, writeTimeout, IMMEDIATE, () -> ApplySerializers.reply, RESPONSE_HANDLER ),
ACCORD_APPLY_REQ (130, P2, writeTimeout, IMMEDIATE, () -> ApplySerializers.request, AccordService::verbHandlerOrNoop, ACCORD_APPLY_RSP ), ACCORD_APPLY_REQ (130, P2, writeTimeout, IMMEDIATE, () -> ApplySerializers.request, AccordService::verbHandlerOrNoop, ACCORD_APPLY_RSP ),
ACCORD_BEGIN_RECOVER_RSP (131, P2, writeTimeout, REQUEST_RESPONSE, () -> RecoverySerializers.reply, RESPONSE_HANDLER ), ACCORD_BEGIN_RECOVER_RSP (131, P2, writeTimeout, IMMEDIATE, () -> RecoverySerializers.reply, RESPONSE_HANDLER ),
ACCORD_BEGIN_RECOVER_REQ (132, P2, writeTimeout, IMMEDIATE, () -> RecoverySerializers.request, AccordService::verbHandlerOrNoop, ACCORD_BEGIN_RECOVER_RSP ), ACCORD_BEGIN_RECOVER_REQ (132, P2, writeTimeout, IMMEDIATE, () -> RecoverySerializers.request, AccordService::verbHandlerOrNoop, ACCORD_BEGIN_RECOVER_RSP ),
ACCORD_BEGIN_INVALIDATE_RSP (133, P2, writeTimeout, REQUEST_RESPONSE, () -> BeginInvalidationSerializers.reply, RESPONSE_HANDLER ), ACCORD_BEGIN_INVALIDATE_RSP (133, P2, writeTimeout, IMMEDIATE, () -> BeginInvalidationSerializers.reply, RESPONSE_HANDLER ),
ACCORD_BEGIN_INVALIDATE_REQ (134, P2, writeTimeout, IMMEDIATE, () -> BeginInvalidationSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_BEGIN_INVALIDATE_RSP ), ACCORD_BEGIN_INVALIDATE_REQ (134, P2, writeTimeout, IMMEDIATE, () -> BeginInvalidationSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_BEGIN_INVALIDATE_RSP ),
ACCORD_WAIT_ON_COMMIT_RSP (136, P2, writeTimeout, REQUEST_RESPONSE, () -> WaitOnCommitSerializer.reply, RESPONSE_HANDLER ), ACCORD_WAIT_ON_COMMIT_RSP (136, P2, writeTimeout, IMMEDIATE, () -> WaitOnCommitSerializer.reply, RESPONSE_HANDLER ),
ACCORD_WAIT_ON_COMMIT_REQ (135, P2, writeTimeout, IMMEDIATE, () -> WaitOnCommitSerializer.request, AccordService::verbHandlerOrNoop, ACCORD_WAIT_ON_COMMIT_RSP ), ACCORD_WAIT_ON_COMMIT_REQ (135, P2, writeTimeout, IMMEDIATE, () -> WaitOnCommitSerializer.request, AccordService::verbHandlerOrNoop, ACCORD_WAIT_ON_COMMIT_RSP ),
ACCORD_WAIT_UNTIL_APPLIED_REQ (137, P2, writeTimeout, IMMEDIATE, () -> ReadDataSerializers.waitUntilApplied, AccordService::verbHandlerOrNoop, ACCORD_READ_RSP ), ACCORD_WAIT_UNTIL_APPLIED_REQ (137, P2, writeTimeout, IMMEDIATE, () -> ReadDataSerializers.waitUntilApplied, AccordService::verbHandlerOrNoop, ACCORD_READ_RSP ),
ACCORD_INFORM_OF_TXN_REQ (138, P2, writeTimeout, IMMEDIATE, () -> InformOfTxnIdSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_SIMPLE_RSP ), ACCORD_INFORM_OF_TXN_REQ (138, P2, writeTimeout, IMMEDIATE, () -> InformOfTxnIdSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_SIMPLE_RSP ),
ACCORD_INFORM_HOME_DURABLE_REQ (139, P2, writeTimeout, IMMEDIATE, () -> InformHomeDurableSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_SIMPLE_RSP ), ACCORD_INFORM_HOME_DURABLE_REQ (139, P2, writeTimeout, IMMEDIATE, () -> InformHomeDurableSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_SIMPLE_RSP ),
ACCORD_INFORM_DURABLE_REQ (140, P2, writeTimeout, IMMEDIATE, () -> InformDurableSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_SIMPLE_RSP ), ACCORD_INFORM_DURABLE_REQ (140, P2, writeTimeout, IMMEDIATE, () -> InformDurableSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_SIMPLE_RSP ),
ACCORD_CHECK_STATUS_RSP (141, P2, writeTimeout, REQUEST_RESPONSE, () -> CheckStatusSerializers.reply, RESPONSE_HANDLER ), ACCORD_CHECK_STATUS_RSP (141, P2, writeTimeout, IMMEDIATE, () -> CheckStatusSerializers.reply, RESPONSE_HANDLER ),
ACCORD_CHECK_STATUS_REQ (142, P2, writeTimeout, IMMEDIATE, () -> CheckStatusSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_CHECK_STATUS_RSP ), ACCORD_CHECK_STATUS_REQ (142, P2, writeTimeout, IMMEDIATE, () -> CheckStatusSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_CHECK_STATUS_RSP ),
ACCORD_GET_DEPS_RSP (143, P2, writeTimeout, REQUEST_RESPONSE, () -> GetDepsSerializers.reply, RESPONSE_HANDLER ), ACCORD_GET_DEPS_RSP (143, P2, writeTimeout, IMMEDIATE, () -> GetDepsSerializers.reply, RESPONSE_HANDLER ),
ACCORD_GET_DEPS_REQ (144, P2, writeTimeout, IMMEDIATE, () -> GetDepsSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_GET_DEPS_RSP ), ACCORD_GET_DEPS_REQ (144, P2, writeTimeout, IMMEDIATE, () -> GetDepsSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_GET_DEPS_RSP ),
ACCORD_GET_EPHMRL_READ_DEPS_RSP (161, P2, writeTimeout, REQUEST_RESPONSE, () -> GetEphmrlReadDepsSerializers.reply, RESPONSE_HANDLER ), ACCORD_GET_EPHMRL_READ_DEPS_RSP (161, P2, writeTimeout, IMMEDIATE, () -> GetEphmrlReadDepsSerializers.reply, RESPONSE_HANDLER ),
ACCORD_GET_EPHMRL_READ_DEPS_REQ (162, P2, writeTimeout, IMMEDIATE, () -> GetEphmrlReadDepsSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_GET_EPHMRL_READ_DEPS_RSP), ACCORD_GET_EPHMRL_READ_DEPS_REQ (162, P2, writeTimeout, IMMEDIATE, () -> GetEphmrlReadDepsSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_GET_EPHMRL_READ_DEPS_RSP),
ACCORD_GET_MAX_CONFLICT_RSP (163, P2, writeTimeout, REQUEST_RESPONSE, () -> GetMaxConflictSerializers.reply, RESPONSE_HANDLER ), ACCORD_GET_MAX_CONFLICT_RSP (163, P2, writeTimeout, IMMEDIATE, () -> GetMaxConflictSerializers.reply, RESPONSE_HANDLER ),
ACCORD_GET_MAX_CONFLICT_REQ (164, P2, writeTimeout, IMMEDIATE, () -> GetMaxConflictSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_GET_MAX_CONFLICT_RSP), ACCORD_GET_MAX_CONFLICT_REQ (164, P2, writeTimeout, IMMEDIATE, () -> GetMaxConflictSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_GET_MAX_CONFLICT_RSP),
ACCORD_FETCH_DATA_RSP (145, P2, repairTimeout,REQUEST_RESPONSE, () -> FetchSerializers.reply, RESPONSE_HANDLER ), ACCORD_FETCH_DATA_RSP (145, P2, repairTimeout,IMMEDIATE, () -> FetchSerializers.reply, RESPONSE_HANDLER ),
ACCORD_FETCH_DATA_REQ (146, P2, repairTimeout,IMMEDIATE, () -> FetchSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_FETCH_DATA_RSP ), ACCORD_FETCH_DATA_REQ (146, P2, repairTimeout,IMMEDIATE, () -> FetchSerializers.request, AccordService::verbHandlerOrNoop, ACCORD_FETCH_DATA_RSP ),
ACCORD_SET_SHARD_DURABLE_REQ (147, P2, writeTimeout, IMMEDIATE, () -> SetDurableSerializers.shardDurable, AccordService::verbHandlerOrNoop, ACCORD_SIMPLE_RSP ), ACCORD_SET_SHARD_DURABLE_REQ (147, P2, writeTimeout, IMMEDIATE, () -> SetDurableSerializers.shardDurable, AccordService::verbHandlerOrNoop, ACCORD_SIMPLE_RSP ),
ACCORD_SET_GLOBALLY_DURABLE_REQ (148, P2, writeTimeout, IMMEDIATE, () -> SetDurableSerializers.globallyDurable,AccordService::verbHandlerOrNoop, ACCORD_SIMPLE_RSP ), ACCORD_SET_GLOBALLY_DURABLE_REQ (148, P2, writeTimeout, IMMEDIATE, () -> SetDurableSerializers.globallyDurable,AccordService::verbHandlerOrNoop, ACCORD_SIMPLE_RSP ),
ACCORD_QUERY_DURABLE_BEFORE_RSP (149, P2, writeTimeout, REQUEST_RESPONSE, () -> QueryDurableBeforeSerializers.reply, RESPONSE_HANDLER ), ACCORD_QUERY_DURABLE_BEFORE_RSP (149, P2, writeTimeout, IMMEDIATE, () -> QueryDurableBeforeSerializers.reply, RESPONSE_HANDLER ),
ACCORD_QUERY_DURABLE_BEFORE_REQ (150, P2, writeTimeout, IMMEDIATE, () -> QueryDurableBeforeSerializers.request,AccordService::verbHandlerOrNoop, ACCORD_QUERY_DURABLE_BEFORE_RSP ), ACCORD_QUERY_DURABLE_BEFORE_REQ (150, P2, writeTimeout, IMMEDIATE, () -> QueryDurableBeforeSerializers.request,AccordService::verbHandlerOrNoop, ACCORD_QUERY_DURABLE_BEFORE_RSP ),
ACCORD_SYNC_NOTIFY_REQ (151, P2, writeTimeout, IMMEDIATE, () -> Notification.listSerializer, () -> AccordSyncPropagator.verbHandler, ACCORD_SIMPLE_RSP ), ACCORD_SYNC_NOTIFY_REQ (151, P2, writeTimeout, IMMEDIATE, () -> Notification.listSerializer, () -> AccordSyncPropagator.verbHandler, ACCORD_SIMPLE_RSP ),

View File

@ -123,7 +123,7 @@ public class AccordCachingState<K, V> extends IntrusiveLinkedListNode
int estimatedSizeOnHeap(ToLongFunction<V> estimator) int estimatedSizeOnHeap(ToLongFunction<V> estimator)
{ {
shouldUpdateSize = false; shouldUpdateSize = false; // TODO (expected): probably not the safest place to clear need to compute size
return lastQueriedEstimatedSizeOnHeap = Ints.checkedCast(EMPTY_SIZE + estimateStateOnHeapSize(estimator)); return lastQueriedEstimatedSizeOnHeap = Ints.checkedCast(EMPTY_SIZE + estimateStateOnHeapSize(estimator));
} }
@ -204,7 +204,7 @@ public class AccordCachingState<K, V> extends IntrusiveLinkedListNode
protected State<K, V> state(State<K, V> next) protected State<K, V> state(State<K, V> next)
{ {
State<K, V> prev = state; State<K, V> prev = state;
if (prev != next) if (prev != next) // TODO (expected): we change state to transition the cache state machine but often keep payload the same - so shouldn't recompute
shouldUpdateSize = true; shouldUpdateSize = true;
return state = next; return state = next;
} }

View File

@ -42,7 +42,7 @@ import accord.api.Agent;
import accord.api.DataStore; import accord.api.DataStore;
import accord.api.Key; import accord.api.Key;
import accord.api.ProgressLog; import accord.api.ProgressLog;
import accord.impl.CommandsForKey; import accord.local.CommandsForKey;
import accord.impl.CommandsSummary; import accord.impl.CommandsSummary;
import accord.impl.TimestampsForKey; import accord.impl.TimestampsForKey;
import accord.local.Command; import accord.local.Command;
@ -114,8 +114,8 @@ public class AccordCommandStore extends CommandStore implements CacheSize
private final ExecutionOrder executionOrder; private final ExecutionOrder executionOrder;
private final AccordStateCache stateCache; private final AccordStateCache stateCache;
private final AccordStateCache.Instance<TxnId, Command, AccordSafeCommand> commandCache; private final AccordStateCache.Instance<TxnId, Command, AccordSafeCommand> commandCache;
private final AccordStateCache.Instance<RoutableKey, TimestampsForKey, AccordSafeTimestampsForKey> timestampsForKeyCache; private final AccordStateCache.Instance<Key, TimestampsForKey, AccordSafeTimestampsForKey> timestampsForKeyCache;
private final AccordStateCache.Instance<RoutableKey, CommandsForKey, AccordSafeCommandsForKey> commandsForKeyCache; private final AccordStateCache.Instance<Key, CommandsForKey, AccordSafeCommandsForKey> commandsForKeyCache;
private AsyncOperation<?> currentOperation = null; private AsyncOperation<?> currentOperation = null;
private AccordSafeCommandStore current = null; private AccordSafeCommandStore current = null;
private long lastSystemTimestampMicros = Long.MIN_VALUE; private long lastSystemTimestampMicros = Long.MIN_VALUE;
@ -161,7 +161,7 @@ public class AccordCommandStore extends CommandStore implements CacheSize
this::validateCommand, this::validateCommand,
AccordObjectSizes::command); AccordObjectSizes::command);
timestampsForKeyCache = timestampsForKeyCache =
stateCache.instance(RoutableKey.class, stateCache.instance(Key.class,
AccordSafeTimestampsForKey.class, AccordSafeTimestampsForKey.class,
AccordSafeTimestampsForKey::new, AccordSafeTimestampsForKey::new,
this::loadTimestampsForKey, this::loadTimestampsForKey,
@ -169,7 +169,7 @@ public class AccordCommandStore extends CommandStore implements CacheSize
this::validateTimestampsForKey, this::validateTimestampsForKey,
AccordObjectSizes::timestampsForKey); AccordObjectSizes::timestampsForKey);
commandsForKeyCache = commandsForKeyCache =
stateCache.instance(RoutableKey.class, stateCache.instance(Key.class,
AccordSafeCommandsForKey.class, AccordSafeCommandsForKey.class,
AccordSafeCommandsForKey::new, AccordSafeCommandsForKey::new,
this::loadCommandsForKey, this::loadCommandsForKey,
@ -220,7 +220,7 @@ public class AccordCommandStore extends CommandStore implements CacheSize
MessageProvider messageProvider = journal.makeMessageProvider(txnId); MessageProvider messageProvider = journal.makeMessageProvider(txnId);
SerializerSupport.TxnAndDeps txnAndDeps = SerializerSupport.extractTxnAndDeps(status, accepted, messageProvider); SerializerSupport.TxnAndDeps txnAndDeps = SerializerSupport.extractTxnAndDeps(unsafeRangesForEpoch(), status, accepted, messageProvider);
Seekables<?, ?> keys = txnAndDeps.txn.keys(); Seekables<?, ?> keys = txnAndDeps.txn.keys();
if (keys.domain() != Routable.Domain.Range) if (keys.domain() != Routable.Domain.Range)
throw new AssertionError(String.format("Txn keys are not range for %s", txnAndDeps.txn)); throw new AssertionError(String.format("Txn keys are not range for %s", txnAndDeps.txn));
@ -311,12 +311,12 @@ public class AccordCommandStore extends CommandStore implements CacheSize
return commandCache; return commandCache;
} }
public AccordStateCache.Instance<RoutableKey, TimestampsForKey, AccordSafeTimestampsForKey> timestampsForKeyCache() public AccordStateCache.Instance<Key, TimestampsForKey, AccordSafeTimestampsForKey> timestampsForKeyCache()
{ {
return timestampsForKeyCache; return timestampsForKeyCache;
} }
public AccordStateCache.Instance<RoutableKey, CommandsForKey, AccordSafeCommandsForKey> commandsForKeyCache() public AccordStateCache.Instance<Key, CommandsForKey, AccordSafeCommandsForKey> commandsForKeyCache()
{ {
return commandsForKeyCache; return commandsForKeyCache;
} }
@ -466,8 +466,8 @@ public class AccordCommandStore extends CommandStore implements CacheSize
public AccordSafeCommandStore beginOperation(PreLoadContext preLoadContext, public AccordSafeCommandStore beginOperation(PreLoadContext preLoadContext,
Map<TxnId, AccordSafeCommand> commands, Map<TxnId, AccordSafeCommand> commands,
NavigableMap<RoutableKey, AccordSafeTimestampsForKey> timestampsForKeys, NavigableMap<Key, AccordSafeTimestampsForKey> timestampsForKeys,
NavigableMap<RoutableKey, AccordSafeCommandsForKey> commandsForKeys) NavigableMap<Key, AccordSafeCommandsForKey> commandsForKeys)
{ {
Invariants.checkState(current == null); Invariants.checkState(current == null);
commands.values().forEach(AccordSafeState::preExecute); commands.values().forEach(AccordSafeState::preExecute);

View File

@ -31,6 +31,7 @@ import accord.primitives.Range;
import accord.topology.Topology; import accord.topology.Topology;
import accord.utils.RandomSource; import accord.utils.RandomSource;
import org.apache.cassandra.cache.CacheSize; import org.apache.cassandra.cache.CacheSize;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.metrics.AccordStateCacheMetrics; import org.apache.cassandra.metrics.AccordStateCacheMetrics;
import org.apache.cassandra.metrics.CacheSizeMetrics; import org.apache.cassandra.metrics.CacheSizeMetrics;
import org.apache.cassandra.schema.TableId; import org.apache.cassandra.schema.TableId;
@ -47,7 +48,7 @@ public class AccordCommandStores extends CommandStores implements CacheSize
ShardDistributor shardDistributor, ProgressLog.Factory progressLogFactory, AccordJournal journal) ShardDistributor shardDistributor, ProgressLog.Factory progressLogFactory, AccordJournal journal)
{ {
super(time, agent, store, random, shardDistributor, progressLogFactory, AccordCommandStore.factory(journal, new AccordStateCacheMetrics(ACCORD_STATE_CACHE))); super(time, agent, store, random, shardDistributor, progressLogFactory, AccordCommandStore.factory(journal, new AccordStateCacheMetrics(ACCORD_STATE_CACHE)));
setCapacity(maxCacheSize()); setCapacity(DatabaseDescriptor.getAccordCacheSizeInMiB() << 20);
this.cacheSizeMetrics = new CacheSizeMetrics(ACCORD_STATE_CACHE, this); this.cacheSizeMetrics = new CacheSizeMetrics(ACCORD_STATE_CACHE, this);
} }
@ -110,11 +111,6 @@ public class AccordCommandStores extends CommandStores implements CacheSize
forEach(commandStore -> ((AccordSafeCommandStore) commandStore).commandStore().setCapacity(perStore)); forEach(commandStore -> ((AccordSafeCommandStore) commandStore).commandStore().setCapacity(perStore));
} }
private static long maxCacheSize()
{
return 5 << 20; // TODO (required): make configurable
}
@Override @Override
public synchronized Supplier<EpochReady> updateTopology(Node node, Topology newTopology, boolean startSync) public synchronized Supplier<EpochReady> updateTopology(Node node, Topology newTopology, boolean startSync)
{ {

View File

@ -23,6 +23,7 @@ import java.time.Duration;
import accord.config.LocalConfig; import accord.config.LocalConfig;
import org.apache.cassandra.config.Config; import org.apache.cassandra.config.Config;
// TODO (expected): should this be merged with AccordSpec?
public class AccordConfiguration implements LocalConfig public class AccordConfiguration implements LocalConfig
{ {
private final Config config; private final Config config;

View File

@ -21,6 +21,7 @@ import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -570,6 +571,7 @@ public class AccordJournal implements Shutdownable
Key(Timestamp timestamp, Type type) Key(Timestamp timestamp, Type type)
{ {
if (timestamp == null) throw new NullPointerException("Null timestamp for type " + type);
this.timestamp = timestamp; this.timestamp = timestamp;
this.type = type; this.type = type;
} }
@ -1361,6 +1363,19 @@ public class AccordJournal implements Shutdownable
return presentMessages; return presentMessages;
} }
public Set<MessageType> all()
{
Set<Type> types = EnumSet.allOf(Type.class);
Set<Key> keys = new ObjectHashSet<>(types.size() + 1, 0.9f);
for (Type type : types)
keys.add(new Key(txnId, type));
Set<Key> presentKeys = journal.test(keys);
Set<MessageType> presentMessages = new ObjectHashSet<>(presentKeys.size() + 1, 0.9f);
for (Key key : presentKeys)
presentMessages.add(key.type.outgoingType);
return presentMessages;
}
@Override @Override
public PreAccept preAccept() public PreAccept preAccept()
{ {

View File

@ -45,7 +45,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import accord.api.Key; import accord.api.Key;
import accord.impl.CommandsForKey; import accord.local.CommandsForKey;
import accord.impl.TimestampsForKey; import accord.impl.TimestampsForKey;
import accord.local.Command; import accord.local.Command;
import accord.local.Command.WaitingOn; import accord.local.Command.WaitingOn;
@ -593,7 +593,7 @@ public class AccordKeyspace
} }
// TODO (expected): garbage-free filtering, reusing encoding // TODO (expected): garbage-free filtering, reusing encoding
public Row withoutRedundantCommands(PartitionKey key, Row row, TxnId redundantBefore) public Row withoutRedundantCommands(PartitionKey key, Row row, RedundantBefore.Entry redundantBefore)
{ {
Invariants.checkState(row.columnCount() == 1); Invariants.checkState(row.columnCount() == 1);
Cell<?> cell = row.getCell(data); Cell<?> cell = row.getCell(data);
@ -601,7 +601,10 @@ public class AccordKeyspace
return row; return row;
CommandsForKey current = CommandsForKeySerializer.fromBytes(key, cell.buffer()); CommandsForKey current = CommandsForKeySerializer.fromBytes(key, cell.buffer());
CommandsForKey updated = current.withoutRedundant(redundantBefore); if (current == null)
return null;
CommandsForKey updated = current.withRedundantBefore(redundantBefore);
if (current == updated) if (current == updated)
return row; return row;
@ -823,7 +826,7 @@ public class AccordKeyspace
Command.Committed committed = command.asCommitted(); Command.Committed committed = command.asCommitted();
Command.Committed originalCommitted = original != null && original.isCommitted() ? original.asCommitted() : null; Command.Committed originalCommitted = original != null && original.isCommitted() ? original.asCommitted() : null;
if (originalCommitted == null || committed.waitingOn != originalCommitted.waitingOn) if (originalCommitted == null || committed.waitingOn != originalCommitted.waitingOn)
builder.addCell(live(CommandsColumns.waiting_on, timestampMicros, WaitingOnSerializer.serialize(committed.waitingOn))); builder.addCell(live(CommandsColumns.waiting_on, timestampMicros, WaitingOnSerializer.serialize(committed.txnId(), committed.waitingOn)));
} }
Row row = builder.build(); Row row = builder.build();
@ -1190,10 +1193,10 @@ public class AccordKeyspace
Ballot promised = deserializePromisedOrNull(row); Ballot promised = deserializePromisedOrNull(row);
Ballot accepted = deserializeAcceptedOrNull(row); Ballot accepted = deserializeAcceptedOrNull(row);
WaitingOnProvider waitingOn = deserializeWaitingOn(row); WaitingOnProvider waitingOn = deserializeWaitingOn(txnId, row);
MessageProvider messages = commandStore.makeMessageProvider(txnId); MessageProvider messages = commandStore.makeMessageProvider(txnId);
return SerializerSupport.reconstruct(attrs, status, executeAt, promised, accepted, waitingOn, messages); return SerializerSupport.reconstruct(commandStore.unsafeRangesForEpoch(), attrs, status, executeAt, promised, accepted, waitingOn, messages);
} }
catch (Throwable t) catch (Throwable t)
{ {
@ -1271,7 +1274,7 @@ public class AccordKeyspace
return deserializeTimestampOrNull(row.getBlob("accepted_ballot"), Ballot::fromBits); return deserializeTimestampOrNull(row.getBlob("accepted_ballot"), Ballot::fromBits);
} }
private static WaitingOnProvider deserializeWaitingOn(UntypedResultSet.Row row) private static WaitingOnProvider deserializeWaitingOn(TxnId txnId, UntypedResultSet.Row row)
{ {
ByteBuffer bytes = row.getBlob("waiting_on"); ByteBuffer bytes = row.getBlob("waiting_on");
@ -1285,7 +1288,7 @@ public class AccordKeyspace
try try
{ {
return WaitingOnSerializer.deserialize(deps, bytes); return WaitingOnSerializer.deserialize(txnId, deps.keyDeps.keys(), deps.rangeDeps.txnIds(), bytes);
} }
catch (IOException e) catch (IOException e)
{ {

View File

@ -24,11 +24,12 @@ import java.util.function.ToLongFunction;
import accord.api.Key; import accord.api.Key;
import accord.api.Result; import accord.api.Result;
import accord.api.RoutingKey; import accord.api.RoutingKey;
import accord.impl.CommandsForKey; import accord.local.CommandsForKey;
import accord.impl.CommandsForKey.Info; import accord.local.CommandsForKey.TxnInfo;
import accord.impl.TimestampsForKey; import accord.impl.TimestampsForKey;
import accord.local.Command; import accord.local.Command;
import accord.local.Command.WaitingOn; import accord.local.Command.WaitingOn;
import accord.local.CommandsForKey.TxnInfoWithMissing;
import accord.local.CommonAttributes; import accord.local.CommonAttributes;
import accord.local.Node; import accord.local.Node;
import accord.local.SaveStatus; import accord.local.SaveStatus;
@ -305,7 +306,6 @@ public class AccordObjectSizes
return ACCEPTED; return ACCEPTED;
case Committed: case Committed:
case Stable: case Stable:
case ReadyToExecute:
return COMMITTED; return COMMITTED;
case PreApplied: case PreApplied:
case Applied: case Applied:
@ -363,22 +363,25 @@ public class AccordObjectSizes
} }
private static long EMPTY_CFK_SIZE = measure(new CommandsForKey(null)); private static long EMPTY_CFK_SIZE = measure(new CommandsForKey(null));
private static long EMPTY_INFO_SIZE = measure(CommandsForKey.Info.createMock(null, null, null)); private static long EMPTY_INFO_SIZE = measure(TxnInfo.createMock(TxnId.NONE, null, null, null));
private static long EMPTY_INFO_WITH_MISSING_ADDITIONAL_SIZE = measure(TxnInfo.createMock(TxnId.NONE, null, null, null)) - EMPTY_INFO_SIZE;
public static long commandsForKey(CommandsForKey cfk) public static long commandsForKey(CommandsForKey cfk)
{ {
long size = EMPTY_CFK_SIZE; long size = EMPTY_CFK_SIZE;
size += key(cfk.key()); size += key(cfk.key());
size += 2 * ObjectSizes.sizeOfReferenceArray(cfk.size()); size += ObjectSizes.sizeOfReferenceArray(cfk.size());
size += cfk.size() * TIMESTAMP_SIZE; size += cfk.size() * EMPTY_INFO_SIZE;
for (int i = 0 ; i < cfk.size() ; ++i) for (int i = 0 ; i < cfk.size() ; ++i)
{ {
Info info = cfk.info(i); TxnInfo info = cfk.get(i);
if (info.getClass() == CommandsForKey.NoInfo.class) if (info.getClass() != TxnInfoWithMissing.class) continue;
continue; TxnInfoWithMissing infoWithMissing = (TxnInfoWithMissing) info;
if (infoWithMissing.missing.length > 0)
size += EMPTY_INFO_SIZE; {
if (info.missing.length > 0) size += EMPTY_INFO_WITH_MISSING_ADDITIONAL_SIZE;
size += ObjectSizes.sizeOfReferenceArray(info.missing.length); size += ObjectSizes.sizeOfReferenceArray(infoWithMissing.missing.length);
size += infoWithMissing.missing.length * TIMESTAMP_SIZE;
}
} }
return size; return size;
} }

View File

@ -31,8 +31,7 @@ import accord.api.DataStore;
import accord.api.Key; import accord.api.Key;
import accord.api.ProgressLog; import accord.api.ProgressLog;
import accord.impl.AbstractSafeCommandStore; import accord.impl.AbstractSafeCommandStore;
import accord.impl.CommandsForKey; import accord.local.CommandsForKey;
import accord.impl.CommandsForKeys;
import accord.impl.CommandsSummary; import accord.impl.CommandsSummary;
import accord.local.Command; import accord.local.Command;
import accord.local.CommandStores.RangesForEpoch; import accord.local.CommandStores.RangesForEpoch;
@ -41,7 +40,6 @@ import accord.local.PreLoadContext;
import accord.primitives.AbstractKeys; import accord.primitives.AbstractKeys;
import accord.primitives.Deps; import accord.primitives.Deps;
import accord.primitives.Ranges; import accord.primitives.Ranges;
import accord.primitives.RoutableKey;
import accord.primitives.Routables; import accord.primitives.Routables;
import accord.primitives.Seekables; import accord.primitives.Seekables;
import accord.primitives.Timestamp; import accord.primitives.Timestamp;
@ -53,16 +51,16 @@ import static accord.primitives.Routable.Domain.Range;
public class AccordSafeCommandStore extends AbstractSafeCommandStore<AccordSafeCommand, AccordSafeTimestampsForKey, AccordSafeCommandsForKey> public class AccordSafeCommandStore extends AbstractSafeCommandStore<AccordSafeCommand, AccordSafeTimestampsForKey, AccordSafeCommandsForKey>
{ {
private final Map<TxnId, AccordSafeCommand> commands; private final Map<TxnId, AccordSafeCommand> commands;
private final NavigableMap<RoutableKey, AccordSafeCommandsForKey> commandsForKeys; private final NavigableMap<Key, AccordSafeCommandsForKey> commandsForKeys;
private final NavigableMap<RoutableKey, AccordSafeTimestampsForKey> timestampsForKeys; private final NavigableMap<Key, AccordSafeTimestampsForKey> timestampsForKeys;
private final AccordCommandStore commandStore; private final AccordCommandStore commandStore;
private final RangesForEpoch ranges; private final RangesForEpoch ranges;
CommandsForRanges.Updater rangeUpdates = null; CommandsForRanges.Updater rangeUpdates = null;
public AccordSafeCommandStore(PreLoadContext context, public AccordSafeCommandStore(PreLoadContext context,
Map<TxnId, AccordSafeCommand> commands, Map<TxnId, AccordSafeCommand> commands,
NavigableMap<RoutableKey, AccordSafeTimestampsForKey> timestampsForKey, NavigableMap<Key, AccordSafeTimestampsForKey> timestampsForKey,
NavigableMap<RoutableKey, AccordSafeCommandsForKey> commandsForKey, NavigableMap<Key, AccordSafeCommandsForKey> commandsForKey,
AccordCommandStore commandStore) AccordCommandStore commandStore)
{ {
super(context); super(context);
@ -94,7 +92,7 @@ public class AccordSafeCommandStore extends AbstractSafeCommandStore<AccordSafeC
} }
@Override @Override
protected AccordSafeCommandsForKey getCommandsForKeyInternal(RoutableKey key) protected AccordSafeCommandsForKey getCommandsForKeyInternal(Key key)
{ {
return commandsForKeys.get(key); return commandsForKeys.get(key);
} }
@ -106,7 +104,7 @@ public class AccordSafeCommandStore extends AbstractSafeCommandStore<AccordSafeC
} }
@Override @Override
protected AccordSafeCommandsForKey getCommandsForKeyIfLoaded(RoutableKey key) protected AccordSafeCommandsForKey getCommandsForKeyIfLoaded(Key key)
{ {
AccordSafeCommandsForKey cfk = commandStore.commandsForKeyCache().acquireIfLoaded(key); AccordSafeCommandsForKey cfk = commandStore.commandsForKeyCache().acquireIfLoaded(key);
if (cfk != null) cfk.preExecute(); if (cfk != null) cfk.preExecute();
@ -114,7 +112,7 @@ public class AccordSafeCommandStore extends AbstractSafeCommandStore<AccordSafeC
} }
@Override @Override
protected AccordSafeTimestampsForKey getTimestampsForKeyInternal(RoutableKey key) protected AccordSafeTimestampsForKey getTimestampsForKeyInternal(Key key)
{ {
return timestampsForKeys.get(key); return timestampsForKeys.get(key);
} }
@ -126,7 +124,7 @@ public class AccordSafeCommandStore extends AbstractSafeCommandStore<AccordSafeC
} }
@Override @Override
protected AccordSafeTimestampsForKey getTimestampsForKeyIfLoaded(RoutableKey key) protected AccordSafeTimestampsForKey getTimestampsForKeyIfLoaded(Key key)
{ {
AccordSafeTimestampsForKey cfk = commandStore.timestampsForKeyCache().acquireIfLoaded(key); AccordSafeTimestampsForKey cfk = commandStore.timestampsForKeyCache().acquireIfLoaded(key);
if (cfk != null) cfk.preExecute(); if (cfk != null) cfk.preExecute();
@ -177,14 +175,14 @@ public class AccordSafeCommandStore extends AbstractSafeCommandStore<AccordSafeC
Ranges allRanges = ranges.all(); Ranges allRanges = ranges.all();
deps.keyDeps.keys().forEach(allRanges, key -> { deps.keyDeps.keys().forEach(allRanges, key -> {
// TODO (now): batch register to minimise GC // TODO (now): batch register to minimise GC
deps.keyDeps.forEach(key, txnId -> { deps.keyDeps.forEach(key, (txnId, txnIdx) -> {
// TODO (desired, efficiency): this can be made more efficient by batching by epoch // TODO (desired, efficiency): this can be made more efficient by batching by epoch
if (ranges.coordinates(txnId).contains(key)) if (ranges.coordinates(txnId).contains(key))
return; // already coordinates, no need to replicate return; // already coordinates, no need to replicate
if (!ranges.allBefore(txnId.epoch()).contains(key)) if (!ranges.allBefore(txnId.epoch()).contains(key))
return; return;
CommandsForKeys.registerNotWitnessed(this, key, txnId); get(key).registerHistorical(this, txnId);
}); });
}); });
CommandsForRanges commandsForRanges = commandStore.commandsForRanges(); CommandsForRanges commandsForRanges = commandStore.commandsForRanges();
@ -221,7 +219,7 @@ public class AccordSafeCommandStore extends AbstractSafeCommandStore<AccordSafeC
for (Key key : keys) for (Key key : keys)
{ {
if (!slice.contains(key)) continue; if (!slice.contains(key)) continue;
CommandsForKey commands = commandsForKey(key).current(); CommandsForKey commands = get(key).current();
accumulate = map.apply(commands, accumulate); accumulate = map.apply(commands, accumulate);
} }
} }
@ -233,11 +231,11 @@ public class AccordSafeCommandStore extends AbstractSafeCommandStore<AccordSafeC
Routables<?> sliced = keysOrRanges.slice(slice, Routables.Slice.Minimal); Routables<?> sliced = keysOrRanges.slice(slice, Routables.Slice.Minimal);
if (!context.keys().slice(slice, Routables.Slice.Minimal).containsAll(sliced)) if (!context.keys().slice(slice, Routables.Slice.Minimal).containsAll(sliced))
throw new AssertionError("Range(s) detected not present in the PreLoadContext: expected " + context.keys() + " but given " + keysOrRanges); throw new AssertionError("Range(s) detected not present in the PreLoadContext: expected " + context.keys() + " but given " + keysOrRanges);
for (RoutableKey key : timestampsForKeys.keySet()) for (Key key : commandsForKeys.keySet())
{ {
//TODO (duplicate code): this is a repeat of Key... only change is checking contains in range //TODO (duplicate code): this is a repeat of Key... only change is checking contains in range
if (!sliced.contains(key)) continue; if (!sliced.contains(key)) continue;
CommandsForKey commands = commandsForKey(key).current(); CommandsForKey commands = get(key).current();
accumulate = map.apply(commands, accumulate); accumulate = map.apply(commands, accumulate);
} }
} }
@ -263,22 +261,20 @@ public class AccordSafeCommandStore extends AbstractSafeCommandStore<AccordSafeC
} }
@Override @Override
protected void update(Command prev, Command updated, @Nullable Seekables<?, ?> keysOrRanges) protected void update(Command prev, Command updated)
{ {
super.update(prev, updated, keysOrRanges); super.update(prev, updated);
if (updated.txnId().domain() == Range && CommandsForKey.needsUpdate(prev, updated)) if (updated.txnId().domain() == Range && CommandsForKey.needsUpdate(prev, updated))
{ {
Seekables<?, ?> keysOrRanges = updated.keysOrRanges();
if (keysOrRanges == null) keysOrRanges = prev.keysOrRanges();
if (keysOrRanges == null) if (keysOrRanges == null)
{ return;
if (updated.known().isDefinitionKnown()) keysOrRanges = updated.partialTxn().keys();
else if (prev.known().isDefinitionKnown()) keysOrRanges = prev.partialTxn().keys();
else return;
}
List<TxnId> waitingOn;
if (updated.partialDeps() == null) waitingOn = Collections.emptyList(); List<TxnId> waitingOn;
// TODO (required): this is faulty: we cannot simply save the raw transaction ids, as they may be for other ranges // TODO (required): this is faulty: we cannot simply save the raw transaction ids, as they may be for other ranges
if (updated.partialDeps() == null) waitingOn = Collections.emptyList();
else waitingOn = updated.partialDeps().txnIds(); else waitingOn = updated.partialDeps().txnIds();
updateRanges().put(updated.txnId(), (Ranges)keysOrRanges, updated.saveStatus(), updated.executeAt(), waitingOn); updateRanges().put(updated.txnId(), (Ranges)keysOrRanges, updated.saveStatus(), updated.executeAt(), waitingOn);
} }
@ -300,8 +296,8 @@ public class AccordSafeCommandStore extends AbstractSafeCommandStore<AccordSafeC
} }
public void postExecute(Map<TxnId, AccordSafeCommand> commands, public void postExecute(Map<TxnId, AccordSafeCommand> commands,
Map<RoutableKey, AccordSafeTimestampsForKey> timestampsForKey, Map<Key, AccordSafeTimestampsForKey> timestampsForKey,
Map<RoutableKey, AccordSafeCommandsForKey> commandsForKeys Map<Key, AccordSafeCommandsForKey> commandsForKeys
) )
{ {
postExecute(); postExecute();

View File

@ -23,18 +23,17 @@ import java.util.Objects;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import accord.api.Key; import accord.api.Key;
import accord.impl.CommandsForKey; import accord.local.CommandsForKey;
import accord.impl.SafeCommandsForKey; import accord.local.SafeCommandsForKey;
import accord.primitives.RoutableKey;
public class AccordSafeCommandsForKey extends SafeCommandsForKey implements AccordSafeState<RoutableKey, CommandsForKey> public class AccordSafeCommandsForKey extends SafeCommandsForKey implements AccordSafeState<Key, CommandsForKey>
{ {
private boolean invalidated; private boolean invalidated;
private final AccordCachingState<RoutableKey, CommandsForKey> global; private final AccordCachingState<Key, CommandsForKey> global;
private CommandsForKey original; private CommandsForKey original;
private CommandsForKey current; private CommandsForKey current;
public AccordSafeCommandsForKey(AccordCachingState<RoutableKey, CommandsForKey> global) public AccordSafeCommandsForKey(AccordCachingState<Key, CommandsForKey> global)
{ {
super((Key) global.key()); super((Key) global.key());
this.global = global; this.global = global;
@ -83,7 +82,7 @@ public class AccordSafeCommandsForKey extends SafeCommandsForKey implements Acco
} }
@Override @Override
public AccordCachingState<RoutableKey, CommandsForKey> global() public AccordCachingState<Key, CommandsForKey> global()
{ {
checkNotInvalidated(); checkNotInvalidated();
return global; return global;

View File

@ -25,6 +25,8 @@ public interface AccordSafeState<K, V> extends SafeState<V>
{ {
void set(V update); void set(V update);
V original(); V original();
void invalidate();
boolean invalidated();
void preExecute(); void preExecute();
void postExecute(); void postExecute();
AccordCachingState<K, V> global(); AccordCachingState<K, V> global();

View File

@ -26,17 +26,16 @@ import com.google.common.annotations.VisibleForTesting;
import accord.api.Key; import accord.api.Key;
import accord.impl.SafeTimestampsForKey; import accord.impl.SafeTimestampsForKey;
import accord.impl.TimestampsForKey; import accord.impl.TimestampsForKey;
import accord.primitives.RoutableKey;
import accord.primitives.Timestamp; import accord.primitives.Timestamp;
public class AccordSafeTimestampsForKey extends SafeTimestampsForKey implements AccordSafeState<RoutableKey, TimestampsForKey> public class AccordSafeTimestampsForKey extends SafeTimestampsForKey implements AccordSafeState<Key, TimestampsForKey>
{ {
private boolean invalidated; private boolean invalidated;
private final AccordCachingState<RoutableKey, TimestampsForKey> global; private final AccordCachingState<Key, TimestampsForKey> global;
private TimestampsForKey original; private TimestampsForKey original;
private TimestampsForKey current; private TimestampsForKey current;
public AccordSafeTimestampsForKey(AccordCachingState<RoutableKey, TimestampsForKey> global) public AccordSafeTimestampsForKey(AccordCachingState<Key, TimestampsForKey> global)
{ {
super((Key) global.key()); super((Key) global.key());
this.global = global; this.global = global;
@ -71,7 +70,7 @@ public class AccordSafeTimestampsForKey extends SafeTimestampsForKey implements
} }
@Override @Override
public AccordCachingState<RoutableKey, TimestampsForKey> global() public AccordCachingState<Key, TimestampsForKey> global()
{ {
checkNotInvalidated(); checkNotInvalidated();
return global; return global;

View File

@ -127,7 +127,7 @@ public class AccordService implements IAccordService, Shutdownable
private final AccordDataStore dataStore; private final AccordDataStore dataStore;
private final AccordJournal journal; private final AccordJournal journal;
private final CoordinateDurabilityScheduling durabilityScheduling; private final CoordinateDurabilityScheduling durabilityScheduling;
private final AccordVerbHandler<? extends Request> verbHandler; private final AccordVerbHandler<? extends Request> requestHandler;
private final LocalConfig configuration; private final LocalConfig configuration;
@GuardedBy("this") @GuardedBy("this")
private State state = State.INIT; private State state = State.INIT;
@ -296,7 +296,7 @@ public class AccordService implements IAccordService, Shutdownable
configuration); configuration);
this.nodeShutdown = toShutdownable(node); this.nodeShutdown = toShutdownable(node);
this.durabilityScheduling = new CoordinateDurabilityScheduling(node); this.durabilityScheduling = new CoordinateDurabilityScheduling(node);
this.verbHandler = new AccordVerbHandler<>(node, configService, journal); this.requestHandler = new AccordVerbHandler<>(node, configService, journal);
} }
@Override @Override
@ -313,14 +313,14 @@ public class AccordService implements IAccordService, Shutdownable
durabilityScheduling.setShardCycleTime(Ints.checkedCast(DatabaseDescriptor.getAccordShardDurabilityCycle(SECONDS)), SECONDS); durabilityScheduling.setShardCycleTime(Ints.checkedCast(DatabaseDescriptor.getAccordShardDurabilityCycle(SECONDS)), SECONDS);
durabilityScheduling.setTxnIdLag(Ints.checkedCast(DatabaseDescriptor.getAccordScheduleDurabilityTxnIdLag(SECONDS)), TimeUnit.SECONDS); durabilityScheduling.setTxnIdLag(Ints.checkedCast(DatabaseDescriptor.getAccordScheduleDurabilityTxnIdLag(SECONDS)), TimeUnit.SECONDS);
durabilityScheduling.setFrequency(Ints.checkedCast(DatabaseDescriptor.getAccordScheduleDurabilityFrequency(SECONDS)), SECONDS); durabilityScheduling.setFrequency(Ints.checkedCast(DatabaseDescriptor.getAccordScheduleDurabilityFrequency(SECONDS)), SECONDS);
durabilityScheduling.start(); // durabilityScheduling.start();
state = State.STARTED; state = State.STARTED;
} }
@Override @Override
public IVerbHandler<? extends Request> verbHandler() public IVerbHandler<? extends Request> verbHandler()
{ {
return verbHandler; return requestHandler;
} }
@Override @Override

View File

@ -41,7 +41,7 @@ import com.google.common.collect.ImmutableSortedMap;
import accord.api.Key; import accord.api.Key;
import accord.api.RoutingKey; import accord.api.RoutingKey;
import accord.impl.CommandsForKey; import accord.local.CommandsForKey;
import accord.impl.CommandsSummary; import accord.impl.CommandsSummary;
import accord.local.Command; import accord.local.Command;
import accord.local.SaveStatus; import accord.local.SaveStatus;
@ -64,7 +64,6 @@ import org.apache.cassandra.utils.IntervalTree;
import static accord.local.SafeCommandStore.*; import static accord.local.SafeCommandStore.*;
import static accord.local.SafeCommandStore.TestDep.ANY_DEPS; import static accord.local.SafeCommandStore.TestDep.ANY_DEPS;
import static accord.local.SafeCommandStore.TestDep.WITH; import static accord.local.SafeCommandStore.TestDep.WITH;
import static accord.local.SafeCommandStore.TestStartedAt.ANY;
import static accord.local.SafeCommandStore.TestStartedAt.STARTED_BEFORE; import static accord.local.SafeCommandStore.TestStartedAt.STARTED_BEFORE;
import static accord.local.SafeCommandStore.TestStatus.ANY_STATUS; import static accord.local.SafeCommandStore.TestStatus.ANY_STATUS;
import static accord.local.Status.Stable; import static accord.local.Status.Stable;
@ -379,11 +378,13 @@ public class CommandsForRanges
private static Range toRange(Interval<RoutableKey, RangeCommandSummary> interval) private static Range toRange(Interval<RoutableKey, RangeCommandSummary> interval)
{ {
TokenKey start = (TokenKey) interval.min; AccordRoutingKey start = (AccordRoutingKey) interval.min;
TokenKey end = (TokenKey) interval.max; if (!(start instanceof AccordRoutingKey.SentinelKey))
start = new TokenKey(start.table(), start.token().decreaseSlightly());
AccordRoutingKey end = (AccordRoutingKey) interval.max;
// TODO (required, correctness) : accord doesn't support wrap around, so decreaseSlightly may fail in some cases // TODO (required, correctness) : accord doesn't support wrap around, so decreaseSlightly may fail in some cases
// TODO (required, correctness) : this logic is mostly used for testing, so is it actually safe for all partitioners? // TODO (required, correctness) : this logic is mostly used for testing, so is it actually safe for all partitioners?
return new TokenRange(start.withToken(start.token().decreaseSlightly()), end); return new TokenRange(start, end);
} }
@Nullable @Nullable

View File

@ -117,6 +117,9 @@ public class AccordAgent implements Agent
public boolean isExpired(TxnId initiated, long now) public boolean isExpired(TxnId initiated, long now)
{ {
// TODO: should distinguish between reads and writes // TODO: should distinguish between reads and writes
if (initiated.kind().isSyncPoint())
return false;
return now - initiated.hlc() > getReadRpcTimeout(MICROSECONDS); return now - initiated.hlc() > getReadRpcTimeout(MICROSECONDS);
} }

View File

@ -33,12 +33,12 @@ import com.google.common.collect.ImmutableSet;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import accord.api.Key;
import accord.api.RoutingKey; import accord.api.RoutingKey;
import accord.local.KeyHistory; import accord.local.KeyHistory;
import accord.local.PreLoadContext; import accord.local.PreLoadContext;
import accord.primitives.Range; import accord.primitives.Range;
import accord.primitives.Ranges; import accord.primitives.Ranges;
import accord.primitives.RoutableKey;
import accord.primitives.Seekables; import accord.primitives.Seekables;
import accord.primitives.TxnId; import accord.primitives.TxnId;
import accord.utils.Invariants; import accord.utils.Invariants;
@ -121,7 +121,7 @@ public class AsyncLoader
} }
} }
private void referenceAndAssembleReadsForKey(RoutableKey key, private void referenceAndAssembleReadsForKey(Key key,
AsyncOperation.Context context, AsyncOperation.Context context,
List<AsyncChain<?>> listenChains) List<AsyncChain<?>> listenChains)
{ {
@ -157,7 +157,7 @@ public class AsyncLoader
{ {
case Key: case Key:
// cast to Keys fails... // cast to Keys fails...
Iterable<RoutableKey> keys = (Iterable<RoutableKey>) keysOrRanges; Iterable<Key> keys = (Iterable<Key>) keysOrRanges;
keys.forEach(key -> referenceAndAssembleReadsForKey(key, context, chains)); keys.forEach(key -> referenceAndAssembleReadsForKey(key, context, chains));
break; break;
case Range: case Range:
@ -172,7 +172,7 @@ public class AsyncLoader
private AsyncChain<?> referenceAndDispatchReadsForRange(AsyncOperation.Context context) private AsyncChain<?> referenceAndDispatchReadsForRange(AsyncOperation.Context context)
{ {
AsyncChain<Set<? extends RoutableKey>> overlappingKeys = findOverlappingKeys((Ranges) keysOrRanges); AsyncChain<Set<? extends Key>> overlappingKeys = findOverlappingKeys((Ranges) keysOrRanges);
return overlappingKeys.flatMap(keys -> { return overlappingKeys.flatMap(keys -> {
if (keys.isEmpty()) if (keys.isEmpty())
@ -183,14 +183,14 @@ public class AsyncLoader
}, commandStore); }, commandStore);
} }
private AsyncChain<Set<? extends RoutableKey>> findOverlappingKeys(Ranges ranges) private AsyncChain<Set<? extends Key>> findOverlappingKeys(Ranges ranges)
{ {
Invariants.checkArgument(!ranges.isEmpty()); Invariants.checkArgument(!ranges.isEmpty());
List<AsyncChain<Set<PartitionKey>>> chains = new ArrayList<>(ranges.size()); List<AsyncChain<Set<PartitionKey>>> chains = new ArrayList<>(ranges.size());
for (Range range : ranges) for (Range range : ranges)
chains.add(findOverlappingKeys(range)); chains.add(findOverlappingKeys(range));
return AsyncChains.reduce(chains, (a, b) -> ImmutableSet.<RoutableKey>builder().addAll(a).addAll(b).build()); return AsyncChains.reduce(chains, (a, b) -> ImmutableSet.<Key>builder().addAll(a).addAll(b).build());
} }
private AsyncChain<Set<PartitionKey>> findOverlappingKeys(Range range) private AsyncChain<Set<PartitionKey>> findOverlappingKeys(Range range)

View File

@ -30,6 +30,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.slf4j.MDC; import org.slf4j.MDC;
import accord.api.Key;
import accord.local.CommandStore; import accord.local.CommandStore;
import accord.local.PreLoadContext; import accord.local.PreLoadContext;
import accord.local.SafeCommandStore; import accord.local.SafeCommandStore;
@ -67,8 +68,8 @@ public abstract class AsyncOperation<R> extends AsyncChains.Head<R> implements R
static class Context static class Context
{ {
final HashMap<TxnId, AccordSafeCommand> commands = new HashMap<>(); final HashMap<TxnId, AccordSafeCommand> commands = new HashMap<>();
final TreeMap<RoutableKey, AccordSafeTimestampsForKey> timestampsForKey = new TreeMap<>(); final TreeMap<Key, AccordSafeTimestampsForKey> timestampsForKey = new TreeMap<>();
final TreeMap<RoutableKey, AccordSafeCommandsForKey> commandsForKey = new TreeMap<>(); final TreeMap<Key, AccordSafeCommandsForKey> commandsForKey = new TreeMap<>();
void releaseResources(AccordCommandStore commandStore) void releaseResources(AccordCommandStore commandStore)
{ {

View File

@ -132,7 +132,6 @@ public class AccordInteropApply extends Apply implements Command.TransientListen
case PreCommitted: case PreCommitted:
case Committed: case Committed:
case PreApplied: case PreApplied:
case ReadyToExecute:
synchronized (this) synchronized (this)
{ {
waitingOn.set(safeStore.commandStore().id()); waitingOn.set(safeStore.commandStore().id());
@ -249,7 +248,6 @@ public class AccordInteropApply extends Apply implements Command.TransientListen
case PreCommitted: case PreCommitted:
case Committed: case Committed:
case PreApplied: case PreApplied:
case ReadyToExecute:
return; return;
case Applied: case Applied:

View File

@ -24,10 +24,10 @@ import java.util.Arrays;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
import accord.api.Key; import accord.api.Key;
import accord.impl.CommandsForKey; import accord.local.CommandsForKey;
import accord.impl.CommandsForKey.Info; import accord.local.CommandsForKey.TxnInfo;
import accord.impl.CommandsForKey.InternalStatus; import accord.local.CommandsForKey.InternalStatus;
import accord.impl.CommandsForKey.NoInfo; import accord.local.CommandsForKey.Unmanaged;
import accord.local.Node; import accord.local.Node;
import accord.primitives.Routable.Domain; import accord.primitives.Routable.Domain;
import accord.primitives.Timestamp; import accord.primitives.Timestamp;
@ -35,8 +35,10 @@ import accord.primitives.Txn;
import accord.primitives.TxnId; import accord.primitives.TxnId;
import accord.utils.Invariants; import accord.utils.Invariants;
import org.apache.cassandra.db.TypeSizes; import org.apache.cassandra.db.TypeSizes;
import org.apache.cassandra.db.marshal.ByteBufferAccessor;
import org.apache.cassandra.utils.vint.VIntCoding; import org.apache.cassandra.utils.vint.VIntCoding;
import static accord.local.CommandsForKey.NO_PENDING_UNMANAGED;
import static accord.primitives.Txn.Kind.ExclusiveSyncPoint; import static accord.primitives.Txn.Kind.ExclusiveSyncPoint;
import static accord.primitives.Txn.Kind.Read; import static accord.primitives.Txn.Kind.Read;
import static accord.primitives.Txn.Kind.Write; import static accord.primitives.Txn.Kind.Write;
@ -104,23 +106,7 @@ public class CommandsForKeySerializer
{ {
int commandCount = cfk.size(); int commandCount = cfk.size();
if (commandCount == 0) if (commandCount == 0)
{ return ByteBuffer.allocate(1);
// TODO (expected): we should not need to special-case, but best solution here is not to store redundantBefore;
// but this requires some modest deeper changes, so for now special-case serialization when empty
Timestamp redundantBefore = cfk.redundantBefore();
ByteBuffer out = ByteBuffer.allocate(TypeSizes.sizeofUnsignedVInt(0) +
TypeSizes.sizeofUnsignedVInt(redundantBefore.epoch()) +
TypeSizes.sizeofUnsignedVInt(redundantBefore.hlc()) +
TypeSizes.sizeofUnsignedVInt(redundantBefore.flags()) +
TypeSizes.sizeofUnsignedVInt(redundantBefore.node.id));
VIntCoding.writeUnsignedVInt32(0, out);
VIntCoding.writeUnsignedVInt(redundantBefore.epoch(), out);
VIntCoding.writeUnsignedVInt(redundantBefore.hlc(), out);
VIntCoding.writeUnsignedVInt32(redundantBefore.flags(), out);
VIntCoding.writeUnsignedVInt32(redundantBefore.node.id, out);
out.flip();
return out;
}
int[] nodeIds = cachedInts().getInts(Math.min(64, commandCount)); int[] nodeIds = cachedInts().getInts(Math.min(64, commandCount));
try try
@ -129,11 +115,9 @@ public class CommandsForKeySerializer
// whether we have any missing transactions to encode, any executeAt that are not equal to their TxnId // whether we have any missing transactions to encode, any executeAt that are not equal to their TxnId
// and whether there are any non-standard flag bits to encode // and whether there are any non-standard flag bits to encode
boolean hasNonStandardFlags = false; boolean hasNonStandardFlags = false;
int nodeIdCount, missingIdCount = 0, executeAtCount = 0, bitsPerExecuteAtFlags = 0; int nodeIdCount = 0, missingIdCount = 0, executeAtCount = 0, bitsPerExecuteAtFlags = 0;
int bitsPerExecuteAtEpochDelta = 0, bitsPerExecuteAtHlcDelta = 1; // to permit us to use full 64 bits and encode in 5 bits we force at least one hlc bit int bitsPerExecuteAtEpochDelta = 0, bitsPerExecuteAtHlcDelta = 1; // to permit us to use full 64 bits and encode in 5 bits we force at least one hlc bit
{ {
nodeIdCount = 1;
nodeIds[0] = cfk.redundantBefore().node.id;
for (int i = 0 ; i < commandCount ; ++i) for (int i = 0 ; i < commandCount ; ++i)
{ {
if (nodeIdCount + 1 >= nodeIds.length) if (nodeIdCount + 1 >= nodeIds.length)
@ -143,24 +127,19 @@ public class CommandsForKeySerializer
nodeIds = cachedInts().resize(nodeIds, nodeIds.length, nodeIds.length * 2); nodeIds = cachedInts().resize(nodeIds, nodeIds.length, nodeIds.length * 2);
} }
TxnId txnId = cfk.txnId(i); TxnInfo txn = cfk.get(i);
Info info = cfk.info(i);
hasNonStandardFlags |= txnIdFlags(txnId) != STANDARD; hasNonStandardFlags |= txnIdFlags(txn) != STANDARD;
nodeIds[nodeIdCount++] = txnId.node.id; nodeIds[nodeIdCount++] = txn.node.id;
if (info.getClass() == NoInfo.class) missingIdCount += txn.missing().length;
if (txn.executeAt == txn)
continue; continue;
missingIdCount += info.missing.length; nodeIds[nodeIdCount++] = txn.executeAt.node.id;
bitsPerExecuteAtEpochDelta = Math.max(bitsPerExecuteAtEpochDelta, numberOfBitsToRepresent(txn.executeAt.epoch() - txn.epoch()));
if (info.executeAt == txnId) bitsPerExecuteAtHlcDelta = Math.max(bitsPerExecuteAtHlcDelta, numberOfBitsToRepresent(txn.executeAt.hlc() - txn.hlc()));
continue; bitsPerExecuteAtFlags = Math.max(bitsPerExecuteAtFlags, numberOfBitsToRepresent(txn.executeAt.flags()));
nodeIds[nodeIdCount++] = info.executeAt.node.id;
bitsPerExecuteAtEpochDelta = Math.max(bitsPerExecuteAtEpochDelta, numberOfBitsToRepresent(info.executeAt.epoch() - txnId.epoch()));
bitsPerExecuteAtHlcDelta = Math.max(bitsPerExecuteAtHlcDelta, numberOfBitsToRepresent(info.executeAt.hlc() - txnId.hlc()));
bitsPerExecuteAtFlags = Math.max(bitsPerExecuteAtFlags, numberOfBitsToRepresent(info.executeAt.flags()));
executeAtCount += 1; executeAtCount += 1;
} }
nodeIdCount = compact(nodeIds); nodeIdCount = compact(nodeIds);
@ -175,8 +154,8 @@ public class CommandsForKeySerializer
int maxHeaderBits = minHeaderBits; int maxHeaderBits = minHeaderBits;
int totalBytes = 0; int totalBytes = 0;
long prevEpoch = cfk.redundantBefore().epoch(); long prevEpoch = cfk.get(0).epoch();
long prevHlc = cfk.redundantBefore().hlc(); long prevHlc = cfk.get(0).hlc();
int[] bytesHistogram = cachedInts().getInts(12); int[] bytesHistogram = cachedInts().getInts(12);
Arrays.fill(bytesHistogram, 0); Arrays.fill(bytesHistogram, 0);
for (int i = 0 ; i < commandCount ; ++i) for (int i = 0 ; i < commandCount ; ++i)
@ -214,7 +193,7 @@ public class CommandsForKeySerializer
if (hasNonStandardFlags && txnIdFlags(txnId) == RAW) if (hasNonStandardFlags && txnIdFlags(txnId) == RAW)
totalBytes += 2; totalBytes += 2;
Info info = cfk.info(i); TxnInfo info = cfk.get(i);
if (info.status.hasInfo) if (info.status.hasInfo)
headerBits += infoHeaderBits; headerBits += infoHeaderBits;
maxHeaderBits = Math.max(headerBits, maxHeaderBits); maxHeaderBits = Math.max(headerBits, maxHeaderBits);
@ -247,9 +226,11 @@ public class CommandsForKeySerializer
// then pick third number as 75th %ile, but at least 1 less than highest, and one more than second // then pick third number as 75th %ile, but at least 1 less than highest, and one more than second
// finally, ensure third then second are distributed so that there is no more than a gap of 4 between them and the next // finally, ensure third then second are distributed so that there is no more than a gap of 4 between them and the next
int l0 = Math.max(0, Math.min(3, minBasicBytes - headerBytes)); int l0 = Math.max(0, Math.min(3, minBasicBytes - headerBytes));
int l1 = Math.max(l0+1, Math.min(l0+4,Arrays.binarySearch(bytesHistogram, commandCount/4) - headerBytes)); int l1 = Arrays.binarySearch(bytesHistogram, minBasicBytes, maxBasicBytes, commandCount/4);
l1 = Math.max(l0+1, Math.min(l0+4, (l1 < 0 ? -1 - l1 : l1) - headerBytes));
int l3 = Math.max(l1+2, maxBasicBytes - headerBytes); int l3 = Math.max(l1+2, maxBasicBytes - headerBytes);
int l2 = Math.max(l1+1, Math.min(l3-1, Arrays.binarySearch(bytesHistogram, (3*commandCount)/4) - headerBytes)); int l2 = Arrays.binarySearch(bytesHistogram, minBasicBytes, maxBasicBytes,(3*commandCount)/4);
l2 = Math.max(l1+1, Math.min(l3-1, (l2 < 0 ? -1 -l2 : l2) - headerBytes));
while (l3-l2 > 4) ++l2; while (l3-l2 > 4) ++l2;
while (l2-l1 > 4) ++l1; while (l2-l1 > 4) ++l1;
hlcBytesLookup = setHlcBytes(l0, l1, l2, l3); hlcBytesLookup = setHlcBytes(l0, l1, l2, l3);
@ -267,16 +248,13 @@ public class CommandsForKeySerializer
totalBytes += TypeSizes.sizeofUnsignedVInt(nodeIds[i] - nodeIds[i-1]); totalBytes += TypeSizes.sizeofUnsignedVInt(nodeIds[i] - nodeIds[i-1]);
totalBytes += 2; totalBytes += 2;
Arrays.fill(bytesHistogram, minBasicBytes, maxBasicBytes + 1, 0);
cachedInts().forceDiscard(bytesHistogram); cachedInts().forceDiscard(bytesHistogram);
prevEpoch = cfk.redundantBefore().epoch(); prevEpoch = cfk.get(0).epoch();
prevHlc = cfk.redundantBefore().hlc(); prevHlc = cfk.get(0).hlc();
// account for encoding redundantBefore // account for encoding redundantBefore
totalBytes += TypeSizes.sizeofUnsignedVInt(prevEpoch); totalBytes += TypeSizes.sizeofUnsignedVInt(prevEpoch);
totalBytes += TypeSizes.sizeofUnsignedVInt(prevHlc); totalBytes += TypeSizes.sizeofUnsignedVInt(prevHlc);
totalBytes += 2; // flags TODO (expected): pack this along with uniqueIdBits, as usually zero bits should be needed
totalBytes += (bitsPerNodeId+7)/8;
if (missingIdCount + executeAtCount > 0) if (missingIdCount + executeAtCount > 0)
{ {
@ -291,6 +269,20 @@ public class CommandsForKeySerializer
totalBytes += 2; totalBytes += 2;
} }
// count unmanaged bytes
int unmanagedPendingCommitCount = 0;
for (int i = 0 ; i < cfk.unmanagedCount() ; ++i)
{
Unmanaged unmanaged = cfk.getUnmanaged(i);
if (unmanaged.pending == Unmanaged.Pending.COMMIT)
++unmanagedPendingCommitCount;
totalBytes += CommandSerializers.txnId.serializedSize();
// TODO (desired): this could be more efficient, e.g. referencing one of the TxnInfo indexes for timestamp
totalBytes += CommandSerializers.timestamp.serializedSize();
}
totalBytes += TypeSizes.sizeofUnsignedVInt(unmanagedPendingCommitCount);
totalBytes += TypeSizes.sizeofUnsignedVInt(cfk.unmanagedCount() - unmanagedPendingCommitCount);
ByteBuffer out = ByteBuffer.allocate(totalBytes); ByteBuffer out = ByteBuffer.allocate(totalBytes);
VIntCoding.writeUnsignedVInt32(commandCount, out); VIntCoding.writeUnsignedVInt32(commandCount, out);
VIntCoding.writeUnsignedVInt32(nodeIdCount, out); VIntCoding.writeUnsignedVInt32(nodeIdCount, out);
@ -301,8 +293,6 @@ public class CommandsForKeySerializer
VIntCoding.writeUnsignedVInt(prevEpoch, out); VIntCoding.writeUnsignedVInt(prevEpoch, out);
VIntCoding.writeUnsignedVInt(prevHlc, out); VIntCoding.writeUnsignedVInt(prevHlc, out);
out.putShort((short) cfk.redundantBefore().flags());
writeLeastSignificantBytes(Arrays.binarySearch(nodeIds, 0, nodeIdCount, cfk.redundantBefore().node.id), (bitsPerNodeId+7)/8, out);
int executeAtMask = executeAtCount > 0 ? 1 : 0; int executeAtMask = executeAtCount > 0 ? 1 : 0;
int missingDepsMask = missingIdCount > 0 ? 1 : 0; int missingDepsMask = missingIdCount > 0 ? 1 : 0;
@ -311,7 +301,7 @@ public class CommandsForKeySerializer
for (int i = 0 ; i < commandCount ; ++i) for (int i = 0 ; i < commandCount ; ++i)
{ {
TxnId txnId = cfk.txnId(i); TxnId txnId = cfk.txnId(i);
Info info = cfk.info(i); TxnInfo info = cfk.get(i);
InternalStatus status = info.status; InternalStatus status = info.status;
long bits = status.ordinal(); long bits = status.ordinal();
@ -322,7 +312,7 @@ public class CommandsForKeySerializer
bits |= hasExecuteAt << bitIndex; bits |= hasExecuteAt << bitIndex;
bitIndex += statusHasInfo & executeAtMask; bitIndex += statusHasInfo & executeAtMask;
long hasMissingIds = info.missing != CommandsForKey.NO_TXNIDS ? 1 : 0; long hasMissingIds = info.missing() != CommandsForKey.NO_TXNIDS ? 1 : 0;
bits |= hasMissingIds << bitIndex; bits |= hasMissingIds << bitIndex;
bitIndex += statusHasInfo & missingDepsMask; bitIndex += statusHasInfo & missingDepsMask;
@ -392,6 +382,20 @@ public class CommandsForKeySerializer
} }
} }
VIntCoding.writeUnsignedVInt32(unmanagedPendingCommitCount, out);
VIntCoding.writeUnsignedVInt32(cfk.unmanagedCount() - unmanagedPendingCommitCount, out);
Unmanaged.Pending pending = unmanagedPendingCommitCount == 0 ? Unmanaged.Pending.APPLY : Unmanaged.Pending.COMMIT;
for (int i = 0 ; i < cfk.unmanagedCount() ; ++i)
{
Unmanaged unmanaged = cfk.getUnmanaged(i);
Invariants.checkState(unmanaged.pending == pending);
CommandSerializers.txnId.serialize(unmanaged.txnId, out, ByteBufferAccessor.instance, out.position());
out.position(out.position() + CommandSerializers.txnId.serializedSize());
CommandSerializers.timestamp.serialize(unmanaged.waitingUntil, out, ByteBufferAccessor.instance, out.position());
out.position(out.position() + CommandSerializers.timestamp.serializedSize());
if (--unmanagedPendingCommitCount == 0) pending = Unmanaged.Pending.APPLY;
}
if ((executeAtCount | missingIdCount) > 0) if ((executeAtCount | missingIdCount) > 0)
{ {
int bitsPerCommandId = numberOfBitsToRepresent(commandCount); int bitsPerCommandId = numberOfBitsToRepresent(commandCount);
@ -407,21 +411,17 @@ public class CommandsForKeySerializer
for (int i = 0 ; i < commandCount ; ++i) for (int i = 0 ; i < commandCount ; ++i)
{ {
Info info = cfk.info(i); TxnInfo txn = cfk.get(i);
if (info.getClass() == NoInfo.class) if (txn.executeAt != txn)
continue;
TxnId txnId = cfk.txnId(i);
if (info.executeAt != txnId)
{ {
Timestamp executeAt = info.executeAt; Timestamp executeAt = txn.executeAt;
int nodeIdx = Arrays.binarySearch(nodeIds, 0, nodeIdCount, executeAt.node.id); int nodeIdx = Arrays.binarySearch(nodeIds, 0, nodeIdCount, executeAt.node.id);
if (bitsPerExecuteAt <= 64) if (bitsPerExecuteAt <= 64)
{ {
Invariants.checkState(executeAt.epoch() >= txnId.epoch()); Invariants.checkState(executeAt.epoch() >= txn.epoch());
long executeAtBits = executeAt.epoch() - txnId.epoch(); long executeAtBits = executeAt.epoch() - txn.epoch();
int offset = bitsPerExecuteAtEpochDelta; int offset = bitsPerExecuteAtEpochDelta;
executeAtBits |= (executeAt.hlc() - txnId.hlc()) << offset ; executeAtBits |= (executeAt.hlc() - txn.hlc()) << offset ;
offset += bitsPerExecuteAtHlcDelta; offset += bitsPerExecuteAtHlcDelta;
executeAtBits |= ((long)executeAt.flags()) << offset; executeAtBits |= ((long)executeAt.flags()) << offset;
offset += bitsPerExecuteAtFlags; offset += bitsPerExecuteAtFlags;
@ -431,9 +431,9 @@ public class CommandsForKeySerializer
} }
else else
{ {
buffer = flushBits(buffer, bufferCount, executeAt.epoch() - txnId.epoch(), bitsPerExecuteAtEpochDelta, out); buffer = flushBits(buffer, bufferCount, executeAt.epoch() - txn.epoch(), bitsPerExecuteAtEpochDelta, out);
bufferCount = (bufferCount + bitsPerExecuteAtEpochDelta) & 63; bufferCount = (bufferCount + bitsPerExecuteAtEpochDelta) & 63;
buffer = flushBits(buffer, bufferCount, executeAt.hlc() - txnId.hlc(), bitsPerExecuteAtHlcDelta, out); buffer = flushBits(buffer, bufferCount, executeAt.hlc() - txn.hlc(), bitsPerExecuteAtHlcDelta, out);
bufferCount = (bufferCount + bitsPerExecuteAtHlcDelta) & 63; bufferCount = (bufferCount + bitsPerExecuteAtHlcDelta) & 63;
buffer = flushBits(buffer, bufferCount, executeAt.flags(), bitsPerExecuteAtFlags, out); buffer = flushBits(buffer, bufferCount, executeAt.flags(), bitsPerExecuteAtFlags, out);
bufferCount = (bufferCount + bitsPerExecuteAtFlags) & 63; bufferCount = (bufferCount + bitsPerExecuteAtFlags) & 63;
@ -442,16 +442,17 @@ public class CommandsForKeySerializer
} }
} }
if (info.missing.length > 0) TxnId[] missing = txn.missing();
if (missing.length > 0)
{ {
int j = 0; int j = 0;
while (j < info.missing.length - 1) while (j < missing.length - 1)
{ {
int missingId = cfk.indexOf(info.missing[j++]); int missingId = cfk.indexOf(missing[j++]);
buffer = flushBits(buffer, bufferCount, missingId, bitsPerMissingId, out); buffer = flushBits(buffer, bufferCount, missingId, bitsPerMissingId, out);
bufferCount = (bufferCount + bitsPerMissingId) & 63; bufferCount = (bufferCount + bitsPerMissingId) & 63;
} }
int missingId = cfk.indexOf(info.missing[info.missing.length - 1]); int missingId = cfk.indexOf(missing[missing.length - 1]);
missingId |= 1L << bitsPerCommandId; missingId |= 1L << bitsPerCommandId;
buffer = flushBits(buffer, bufferCount, missingId, bitsPerMissingId, out); buffer = flushBits(buffer, bufferCount, missingId, bitsPerMissingId, out);
bufferCount = (bufferCount + bitsPerMissingId) & 63; bufferCount = (bufferCount + bitsPerMissingId) & 63;
@ -461,6 +462,7 @@ public class CommandsForKeySerializer
writeMostSignificantBytes(buffer, (bufferCount + 7)/8, out); writeMostSignificantBytes(buffer, (bufferCount + 7)/8, out);
} }
Invariants.checkState(!out.hasRemaining());
out.flip(); out.flip();
return out; return out;
} }
@ -494,16 +496,10 @@ public class CommandsForKeySerializer
in = in.duplicate(); in = in.duplicate();
int commandCount = VIntCoding.readUnsignedVInt32(in); int commandCount = VIntCoding.readUnsignedVInt32(in);
if (commandCount == 0) if (commandCount == 0)
{ return new CommandsForKey(key);
long epoch = VIntCoding.readUnsignedVInt(in);
long hlc = VIntCoding.readUnsignedVInt(in);
int flags = VIntCoding.readUnsignedVInt32(in);
Node.Id id = new Node.Id(VIntCoding.readUnsignedVInt32(in));
return new CommandsForKey(key).withoutRedundant(TxnId.fromValues(epoch, hlc, flags, id));
}
TxnId[] txnIds = new TxnId[commandCount]; TxnId[] txnIds = cachedTxnIds().get(commandCount);
Info[] infos = new Info[commandCount]; TxnInfo[] txns = new TxnInfo[commandCount];
int nodeIdCount = VIntCoding.readUnsignedVInt32(in); int nodeIdCount = VIntCoding.readUnsignedVInt32(in);
int bitsPerNodeId = numberOfBitsToRepresent(nodeIdCount); int bitsPerNodeId = numberOfBitsToRepresent(nodeIdCount);
long nodeIdMask = (1L << bitsPerNodeId) - 1; long nodeIdMask = (1L << bitsPerNodeId) - 1;
@ -526,12 +522,8 @@ public class CommandsForKeySerializer
hlcBytesLookup = setHlcByteDeltas((flags >>> 5) & 0x3, (flags >>> 7) & 0x3, (flags >>> 9) & 0x3, (flags >>> 11) & 0x3); hlcBytesLookup = setHlcByteDeltas((flags >>> 5) & 0x3, (flags >>> 7) & 0x3, (flags >>> 9) & 0x3, (flags >>> 11) & 0x3);
} }
long prevEpoch = VIntCoding.readUnsignedVInt32(in); long prevEpoch = VIntCoding.readUnsignedVInt(in);
long prevHlc = VIntCoding.readUnsignedVInt32(in); long prevHlc = VIntCoding.readUnsignedVInt(in);
TxnId redundantBefore = TxnId.fromValues(prevEpoch, prevHlc, in.getShort(),
nodeIds[(int)readLeastSignificantBytes((bitsPerNodeId+7)/8, in)]);
for (int i = 0 ; i < commandCount ; ++i) for (int i = 0 ; i < commandCount ; ++i)
{ {
long header = readLeastSignificantBytes(headerByteCount, in); long header = readLeastSignificantBytes(headerByteCount, in);
@ -601,12 +593,34 @@ public class CommandsForKeySerializer
: TxnId.fromValues(epoch, hlc, flags, node); : TxnId.fromValues(epoch, hlc, flags, node);
txnIds[i] = txnId; txnIds[i] = txnId;
infos[i] = DECODE_INFOS[(executeAtInfoOffset | missingDepsInfoOffset)*STATUS_COUNT + status.ordinal()]; txns[i] = DECODE_INFOS[(executeAtInfoOffset | missingDepsInfoOffset)*STATUS_COUNT + status.ordinal()];
prevEpoch = epoch; prevEpoch = epoch;
prevHlc = hlc; prevHlc = hlc;
} }
int unmanagedPendingCommitCount = VIntCoding.readUnsignedVInt32(in);
int unmanagedCount = unmanagedPendingCommitCount + VIntCoding.readUnsignedVInt32(in);
Unmanaged[] unmanageds;
if (unmanagedCount == 0)
{
unmanageds = NO_PENDING_UNMANAGED;
}
else
{
unmanageds = new Unmanaged[unmanagedCount];
Unmanaged.Pending pending = unmanagedPendingCommitCount == 0 ? Unmanaged.Pending.APPLY : Unmanaged.Pending.COMMIT;
for (int i = 0 ; i < unmanagedCount ; ++i)
{
TxnId txnId = CommandSerializers.txnId.deserialize(in, ByteBufferAccessor.instance, in.position());
in.position(in.position() + CommandSerializers.txnId.serializedSize());
Timestamp waitingUntil = CommandSerializers.timestamp.deserialize(in, ByteBufferAccessor.instance, in.position());
in.position(in.position() + CommandSerializers.timestamp.serializedSize());
unmanageds[i] = new Unmanaged(pending, txnId, waitingUntil);
if (--unmanagedPendingCommitCount == 0) pending = Unmanaged.Pending.APPLY;
}
}
if (executeAtMasks + missingDepsMasks > 0) if (executeAtMasks + missingDepsMasks > 0)
{ {
TxnId[] missingIdBuffer = cachedTxnIds().get(8); TxnId[] missingIdBuffer = cachedTxnIds().get(8);
@ -630,13 +644,10 @@ public class CommandsForKeySerializer
for (int i = 0 ; i < commandCount ; ++i) for (int i = 0 ; i < commandCount ; ++i)
{ {
Info info = infos[i];
if (info.getClass() == NoInfo.class)
continue;
TxnId txnId = txnIds[i]; TxnId txnId = txnIds[i];
Timestamp executeAt = txnId; TxnInfo placeholder = txns[i];
if (info.executeAt == null) Timestamp executeAt;
if (placeholder.executeAt == null)
{ {
long epoch, hlc; long epoch, hlc;
int flags; int flags;
@ -661,8 +672,12 @@ public class CommandsForKeySerializer
} }
executeAt = Timestamp.fromValues(epoch, hlc, flags, id); executeAt = Timestamp.fromValues(epoch, hlc, flags, id);
} }
else
{
executeAt = txnId;
}
TxnId[] missing = info.missing; TxnId[] missing = placeholder.missing();
if (missing == null) if (missing == null)
{ {
int prev = -1; int prev = -1;
@ -684,13 +699,19 @@ public class CommandsForKeySerializer
missingIdCount = 0; missingIdCount = 0;
} }
infos[i] = Info.create(txnId, info.status, executeAt, missing); txns[i] = TxnInfo.create(txnId, placeholder.status, executeAt, missing);
} }
cachedTxnIds().forceDiscard(missingIdBuffer, maxIdBufferCount); cachedTxnIds().forceDiscard(missingIdBuffer, maxIdBufferCount);
} }
else
{
for (int i = 0 ; i < commandCount ; ++i)
txns[i] = TxnInfo.create(txnIds[i], txns[i].status, txnIds[i]);
}
cachedTxnIds().forceDiscard(txnIds, commandCount);
return CommandsForKey.SerializerSupport.create(key, redundantBefore, txnIds, infos); return CommandsForKey.SerializerSupport.create(key, txns, unmanageds);
} }
private static int getHlcBytes(int lookup, int index) private static int getHlcBytes(int lookup, int index)
@ -833,16 +854,16 @@ public class CommandsForKeySerializer
private static final Txn.Kind[] TXN_ID_FLAG_BITS_KIND_LOOKUP = new Txn.Kind[] { Read, Write, ExclusiveSyncPoint, null }; private static final Txn.Kind[] TXN_ID_FLAG_BITS_KIND_LOOKUP = new Txn.Kind[] { Read, Write, ExclusiveSyncPoint, null };
private static final int STATUS_COUNT = InternalStatus.values().length; private static final int STATUS_COUNT = InternalStatus.values().length;
private static final Info[] DECODE_INFOS = new Info[4 * STATUS_COUNT]; private static final TxnInfo[] DECODE_INFOS = new TxnInfo[4 * STATUS_COUNT];
static static
{ {
for (InternalStatus status : InternalStatus.values()) for (InternalStatus status : InternalStatus.values())
{ {
int ordinal = status.ordinal(); int ordinal = status.ordinal();
DECODE_INFOS[ordinal] = status.asNoInfo; DECODE_INFOS[ordinal] = TxnInfo.createMock(TxnId.NONE, status, TxnId.NONE, CommandsForKey.NO_TXNIDS);
DECODE_INFOS[STATUS_COUNT+ordinal] = Info.createMock(status, Timestamp.NONE, null); DECODE_INFOS[STATUS_COUNT+ordinal] = TxnInfo.createMock(TxnId.NONE, status, TxnId.NONE, null);
DECODE_INFOS[2*STATUS_COUNT+ordinal] = Info.createMock(status, null, CommandsForKey.NO_TXNIDS); DECODE_INFOS[2*STATUS_COUNT+ordinal] = TxnInfo.createMock(TxnId.NONE, status, null, CommandsForKey.NO_TXNIDS);
DECODE_INFOS[3*STATUS_COUNT+ordinal] = Info.createMock(status, null, null); DECODE_INFOS[3*STATUS_COUNT+ordinal] = TxnInfo.createMock(TxnId.NONE, status, null, null);
} }
} }

View File

@ -22,40 +22,63 @@ import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import accord.local.Command.WaitingOn; import accord.local.Command.WaitingOn;
import accord.primitives.Deps; import accord.primitives.Keys;
import accord.primitives.Routable;
import accord.primitives.TxnId;
import accord.utils.ImmutableBitSet; import accord.utils.ImmutableBitSet;
import accord.utils.Invariants; import accord.utils.Invariants;
import accord.utils.SimpleBitSet; import accord.utils.SimpleBitSet;
import accord.utils.SortedArrays.SortedArrayList;
import org.apache.cassandra.db.TypeSizes; import org.apache.cassandra.db.TypeSizes;
import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataInputPlus;
import org.apache.cassandra.io.util.DataOutputPlus; import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.utils.vint.VIntCoding;
public class WaitingOnSerializer public class WaitingOnSerializer
{ {
public static void serialize(WaitingOn waitingOn, DataOutputPlus out) throws IOException public static void serialize(TxnId txnId, WaitingOn waitingOn, DataOutputPlus out) throws IOException
{ {
// TODO (expected): use run length encoding; we know that at most 1/3rd of bits will be set between the three bitsets out.writeUnsignedVInt32(waitingOn.keys.size());
int length = (waitingOn.deps.txnIdCount() + 63) / 64; out.writeUnsignedVInt32(waitingOn.txnIds.size());
serialize(length, waitingOn.waitingOnCommit, out); int keyCount = waitingOn.keys.size();
serialize(length, waitingOn.waitingOnApply, out); int txnIdCount = waitingOn.txnIds.size();
serialize(length, waitingOn.appliedOrInvalidated, out); int waitingOnLength = (txnIdCount + keyCount + 63) / 64;
serialize(waitingOnLength, waitingOn.waitingOn, out);
if (txnId.domain() == Routable.Domain.Range)
{
int appliedOrInvalidatedLength = (txnIdCount + 63) / 64;
serialize(appliedOrInvalidatedLength, waitingOn.appliedOrInvalidated, out);
}
} }
public static WaitingOn deserialize(Deps deps, DataInputPlus in) throws IOException public static WaitingOn deserialize(TxnId txnId, Keys keys, SortedArrayList<TxnId> txnIds, DataInputPlus in) throws IOException
{ {
int length = (deps.txnIdCount() + 63) / 64; int a = in.readUnsignedVInt32();
ImmutableBitSet waitingOnCommit = deserialize(length, in); int b = in.readUnsignedVInt32();
ImmutableBitSet waitingOnApply = deserialize(length, in); int waitingOnLength = (txnIds.size() + keys.size() + 63) / 64;
ImmutableBitSet appliedOrInvalidated = deserialize(length, in); ImmutableBitSet waitingOn = deserialize(waitingOnLength, in);
return new WaitingOn(deps, waitingOnCommit, waitingOnApply, appliedOrInvalidated); ImmutableBitSet appliedOrInvalidated = null;
if (txnId.domain() == Routable.Domain.Range)
{
int appliedOrInvalidatedLength = (txnIds.size() + 63) / 64;
appliedOrInvalidated = deserialize(appliedOrInvalidatedLength, in);
}
return new WaitingOn(keys, txnIds, waitingOn, appliedOrInvalidated);
} }
public static long serializedSize(WaitingOn waitingOn) public static long serializedSize(WaitingOn waitingOn)
{ {
int length = (waitingOn.deps.txnIdCount() + 63) / 64; int keyCount = waitingOn.keys.size();
return serializedSize(length, waitingOn.waitingOnCommit) int txnIdCount = waitingOn.txnIds.size();
+ serializedSize(length, waitingOn.waitingOnApply) int waitingOnLength = (txnIdCount + keyCount + 63) / 64;
+ serializedSize(length, waitingOn.appliedOrInvalidated); long size = serializedSize(waitingOnLength, waitingOn.waitingOn);
size += TypeSizes.sizeofUnsignedVInt(keyCount);
size += TypeSizes.sizeofUnsignedVInt(txnIdCount);
if (waitingOn.appliedOrInvalidated == null)
return size;
int appliedOrInvalidatedLength = (txnIdCount + 63) / 64;
return size + serializedSize(appliedOrInvalidatedLength, waitingOn.appliedOrInvalidated);
} }
private static void serialize(int length, SimpleBitSet write, DataOutputPlus out) throws IOException private static void serialize(int length, SimpleBitSet write, DataOutputPlus out) throws IOException
@ -81,14 +104,23 @@ public class WaitingOnSerializer
return (long) TypeSizes.LONG_SIZE * length; return (long) TypeSizes.LONG_SIZE * length;
} }
public static ByteBuffer serialize(WaitingOn waitingOn) throws IOException public static ByteBuffer serialize(TxnId txnId, WaitingOn waitingOn) throws IOException
{ {
int length = (waitingOn.deps.txnIdCount() + 63) / 64; int keyCount = waitingOn.keys.size();
ByteBuffer out = ByteBuffer.allocate(TypeSizes.LONG_SIZE * length * 3); int txnIdCount = waitingOn.txnIds.size();
serialize(length, waitingOn.waitingOnCommit, out); int waitingOnLength = (txnIdCount + keyCount + 63) / 64;
serialize(length, waitingOn.waitingOnApply, out); int appliedOrInvalidatedLength = 0;
serialize(length, waitingOn.appliedOrInvalidated, out); if (txnId.domain() == Routable.Domain.Range)
return (ByteBuffer) out.flip(); appliedOrInvalidatedLength = (txnIdCount + 63) / 64;
ByteBuffer out = ByteBuffer.allocate(TypeSizes.sizeofUnsignedVInt(keyCount) + TypeSizes.sizeofUnsignedVInt(txnIdCount)
+ TypeSizes.LONG_SIZE * (waitingOnLength + appliedOrInvalidatedLength));
VIntCoding.writeUnsignedVInt32(keyCount, out);
VIntCoding.writeUnsignedVInt32(txnIdCount, out);
serialize(waitingOnLength, waitingOn.waitingOn, out);
if (appliedOrInvalidatedLength > 0)
serialize(appliedOrInvalidatedLength, waitingOn.appliedOrInvalidated, out);
return out.flip();
} }
private static void serialize(int length, SimpleBitSet write, ByteBuffer out) private static void serialize(int length, SimpleBitSet write, ByteBuffer out)
@ -99,16 +131,23 @@ public class WaitingOnSerializer
out.putLong(bits[i]); out.putLong(bits[i]);
} }
public static WaitingOn deserialize(Deps deps, ByteBuffer in) throws IOException public static WaitingOn deserialize(TxnId txnId, Keys keys, SortedArrayList<TxnId> txnIds, ByteBuffer in) throws IOException
{ {
int length = (deps.txnIdCount() + 63) / 64; int waitingOnLength = (txnIds.size() + keys.size() + 63) / 64;
int position = in.position(); int position = in.position();
ImmutableBitSet waitingOnCommit = deserialize(position, length, in); int a = VIntCoding.readUnsignedVInt32(in, position);
position += length*8; position += TypeSizes.sizeofUnsignedVInt(a);
ImmutableBitSet waitingOnApply = deserialize(position, length, in); int b = VIntCoding.readUnsignedVInt32(in, position);
position += length*8; position += TypeSizes.sizeofUnsignedVInt(a);
ImmutableBitSet appliedOrInvalidated = deserialize(position, length, in); ImmutableBitSet waitingOn = deserialize(position, waitingOnLength, in);
return new WaitingOn(deps, waitingOnCommit, waitingOnApply, appliedOrInvalidated); ImmutableBitSet appliedOrInvalidated = null;
if (txnId.domain() == Routable.Domain.Range)
{
position += waitingOnLength*8;
int appliedOrInvalidatedLength = (txnIds.size() + 63) / 64;
appliedOrInvalidated = deserialize(position, appliedOrInvalidatedLength, in);
}
return new WaitingOn(keys, txnIds, waitingOn, appliedOrInvalidated);
} }
private static ImmutableBitSet deserialize(int position, int length, ByteBuffer in) private static ImmutableBitSet deserialize(int position, int length, ByteBuffer in)

View File

@ -35,9 +35,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import accord.api.DataStore; import accord.api.DataStore;
import accord.api.Key;
import accord.api.Write; import accord.api.Write;
import accord.impl.AbstractSafeCommandStore; import accord.impl.AbstractSafeCommandStore;
import accord.impl.CommandsForKeys; import accord.impl.TimestampsForKeys;
import accord.impl.TimestampsForKey; import accord.impl.TimestampsForKey;
import accord.local.SafeCommandStore; import accord.local.SafeCommandStore;
import accord.primitives.PartialTxn; import accord.primitives.PartialTxn;
@ -375,7 +376,7 @@ public class TxnWrite extends AbstractKeySorted<TxnWrite.Update> implements Writ
// TODO (expected, efficiency): 99.9999% of the time we can just use executeAt.hlc(), so can avoid bringing // TODO (expected, efficiency): 99.9999% of the time we can just use executeAt.hlc(), so can avoid bringing
// cfk into memory by retaining at all times in memory key ranges that are dirty and must use this logic; // cfk into memory by retaining at all times in memory key ranges that are dirty and must use this logic;
// any that aren't can just use executeAt.hlc // any that aren't can just use executeAt.hlc
TimestampsForKey cfk = CommandsForKeys.updateLastExecutionTimestamps((AbstractSafeCommandStore<?,?,?>) safeStore, (RoutableKey) key, executeAt, true); TimestampsForKey cfk = TimestampsForKeys.updateLastExecutionTimestamps((AbstractSafeCommandStore<?,?,?>) safeStore, (Key) key, executeAt, true);
long timestamp = AccordSafeTimestampsForKey.timestampMicrosFor(cfk, executeAt, true); long timestamp = AccordSafeTimestampsForKey.timestampMicrosFor(cfk, executeAt, true);
// TODO (low priority - do we need to compute nowInSeconds, or can we just use executeAt?) // TODO (low priority - do we need to compute nowInSeconds, or can we just use executeAt?)
int nowInSeconds = AccordSafeTimestampsForKey.nowInSecondsFor(cfk, executeAt, true); int nowInSeconds = AccordSafeTimestampsForKey.nowInSecondsFor(cfk, executeAt, true);

View File

@ -126,13 +126,27 @@ public class VIntCoding
return retval; return retval;
} }
@DontInline
private static long readUnsignedVIntSlow(ByteBuffer in, int position, byte firstByte)
{
int size = numberOfExtraBytesToRead(firstByte);
long retval = firstByte & firstByteValueMask(size);
for (int ii = 0; ii < size; ii++)
{
byte b = in.get(position++);
retval <<= 8;
retval |= b & 0xff;
}
return retval;
}
public static long readUnsignedVInt(ByteBuffer in) public static long readUnsignedVInt(ByteBuffer in)
{ {
byte firstByte = in.get(); byte firstByte = in.get();
if (firstByte >= 0) if (firstByte >= 0)
return firstByte; return firstByte;
int position = in.position(); int position = in.position();
int limit = in.limit(); int limit = in.limit();
if (limit - position < 8) if (limit - position < 8)
@ -155,6 +169,32 @@ public class VIntCoding
return retval; return retval;
} }
public static long readUnsignedVInt(ByteBuffer in, int position)
{
byte firstByte = in.get(position++);
if (firstByte >= 0)
return firstByte;
int limit = in.limit();
if (limit - position < 8)
return readUnsignedVIntSlow(in, position, firstByte);
int extraBytes = VIntCoding.numberOfExtraBytesToRead(firstByte);
int extraBits = extraBytes * 8;
long retval = in.getLong(position);
if (in.order() == ByteOrder.LITTLE_ENDIAN)
retval = Long.reverseBytes(retval);
// truncate the bytes we read in excess of those we needed
retval >>>= 64 - extraBits;
// remove the non-value bits from the first byte
firstByte &= VIntCoding.firstByteValueMask(extraBytes);
// shift the first byte up to its correct position
retval |= (long) firstByte << extraBits;
return retval;
}
public static void skipUnsignedVInt(DataInputPlus input) throws IOException public static void skipUnsignedVInt(DataInputPlus input) throws IOException
{ {
int firstByte = input.readByte(); int firstByte = input.readByte();
@ -330,6 +370,11 @@ public class VIntCoding
return checkedCast(readUnsignedVInt(input)); return checkedCast(readUnsignedVInt(input));
} }
public static int readUnsignedVInt32(ByteBuffer input, int position)
{
return checkedCast(readUnsignedVInt(input, position));
}
// & this with the first byte to give the value part for a given extraBytesToRead encoded in the byte // & this with the first byte to give the value part for a given extraBytesToRead encoded in the byte
public static int firstByteValueMask(int extraBytesToRead) public static int firstByteValueMask(int extraBytesToRead)
{ {

View File

@ -0,0 +1,56 @@
<!--
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.
-->
<configuration debug="false" scan="true" scanPeriod="60 seconds">
<define name="cluster_id" class="org.apache.cassandra.distributed.impl.ClusterIDDefiner" />
<define name="instance_id" class="org.apache.cassandra.distributed.impl.InstanceIDDefiner" />
<!-- Shutdown hook ensures that async appender flushes -->
<shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/>
<appender name="INSTANCEFILE" class="ch.qos.logback.core.FileAppender">
<file>./build/test/logs/${cassandra.testtag}/${suitename}/${cluster_id}/${instance_id}/system.log</file>
<encoder>
<pattern>%-5level [%thread] ${instance_id} %date{ISO8601} %F:%L - %msg%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<immediateFlush>true</immediateFlush>
</appender>
<appender name="INSTANCESTDERR" target="System.err" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-5level %date{HH:mm:ss,SSS} %msg%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>
<!-- Un-comment to enable TRACE logging for Accord transactions.
<logger name="accord" level="TRACE" />
<logger name="org.apache.cassandra.service.accord" level="TRACE" />
-->
<root level="DEBUG">
<appender-ref ref="INSTANCEFILE" /> <!-- use blocking to avoid race conditions with appending and searching -->
<appender-ref ref="INSTANCESTDERR" />
</root>
</configuration>

View File

@ -21,6 +21,7 @@ package org.apache.cassandra.distributed.api;
import java.util.Iterator; import java.util.Iterator;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.function.BiConsumer;
import org.apache.cassandra.distributed.shared.FutureUtils; import org.apache.cassandra.distributed.shared.FutureUtils;
@ -60,6 +61,8 @@ public interface ICoordinator
} }
SimpleQueryResult executeWithResult(String query, ConsistencyLevel consistencyLevel, Object... boundValues); SimpleQueryResult executeWithResult(String query, ConsistencyLevel consistencyLevel, Object... boundValues);
Future<?> executeWithResult(BiConsumer<SimpleQueryResult, Throwable> callback, String query, ConsistencyLevel consistencyLevel, Object... boundValues);
Future<?> executeWithResult(BiConsumer<SimpleQueryResult, Throwable> callback, String query, ConsistencyLevel serialConsistencyLevel, ConsistencyLevel commitConsistencyLevel, Object... boundValues);
default SimpleQueryResult executeWithResult(String query, ConsistencyLevel serialConsistencyLevel, ConsistencyLevel commitConsistencyLevel, Object... boundValues) default SimpleQueryResult executeWithResult(String query, ConsistencyLevel serialConsistencyLevel, ConsistencyLevel commitConsistencyLevel, Object... boundValues)
{ {

View File

@ -25,6 +25,7 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.function.BiConsumer;
import com.google.common.collect.Iterators; import com.google.common.collect.Iterators;
@ -62,6 +63,30 @@ public class Coordinator implements ICoordinator
return instance().sync(() -> unsafeExecuteInternal(query, consistencyLevel, boundValues)).call(); return instance().sync(() -> unsafeExecuteInternal(query, consistencyLevel, boundValues)).call();
} }
@Override
public Future<?> executeWithResult(BiConsumer<SimpleQueryResult, Throwable> callback, String query, ConsistencyLevel consistencyLevel, Object... boundValues)
{
return executeWithResult(callback, query, null, consistencyLevel, boundValues);
}
@Override
public Future<?> executeWithResult(BiConsumer<SimpleQueryResult, Throwable> callback, String query, ConsistencyLevel serialConsistencyLevel, ConsistencyLevel commitConsistencyLevel, Object... boundValues)
{
return instance().async(cb -> {
SimpleQueryResult result;
try
{
result = CoordinatorHelper.unsafeExecuteInternal(query, serialConsistencyLevel, commitConsistencyLevel, boundValues);
}
catch (Throwable t)
{
callback.accept(null, t);
return;
}
callback.accept(result, null);
}).apply(callback);
}
public Future<SimpleQueryResult> asyncExecuteWithTracingWithResult(UUID sessionId, String query, ConsistencyLevel consistencyLevelOrigin, Object... boundValues) public Future<SimpleQueryResult> asyncExecuteWithTracingWithResult(UUID sessionId, String query, ConsistencyLevel consistencyLevelOrigin, Object... boundValues)
{ {
return instance.async(() -> { return instance.async(() -> {

View File

@ -507,6 +507,7 @@ public class Instance extends IsolatedExecutor implements IInvokableInstance
public void receiveMessage(IMessage message) public void receiveMessage(IMessage message)
{ {
sync(receiveMessageRunnable(message)).accept(false); sync(receiveMessageRunnable(message)).accept(false);
// async(receiveMessageRunnable(message)).apply(false);
} }
@Override @Override

View File

@ -0,0 +1,96 @@
/*
* 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.cassandra.distributed.test.accord;
import java.io.IOException;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.Semaphore;
import com.google.common.util.concurrent.RateLimiter;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.distributed.api.ConsistencyLevel;
import org.apache.cassandra.distributed.api.ICoordinator;
import org.apache.cassandra.utils.EstimatedHistogram;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
public class AccordLoadTest extends AccordTestBase
{
private static final Logger logger = LoggerFactory.getLogger(AccordLoadTest.class);
@BeforeClass
public static void setUp() throws IOException
{
AccordTestBase.setupCluster(builder -> builder.withConfig(config -> config.set("lwt_strategy", "accord").set("non_serial_write_strategy", "accord")), 2);
}
@Ignore
@Test
public void testLoad() throws Exception
{
test("CREATE TABLE " + qualifiedTableName + " (k int, v int, PRIMARY KEY(k))",
cluster -> {
ICoordinator coordinator = cluster.coordinator(1);
final int batchSize = 1000;
final int concurrency = 100;
final int ratePerSecond = 1000;
final int keyCount = 10;
for (int i = 1; i <= keyCount; i++)
coordinator.execute("INSERT INTO " + qualifiedTableName + " (k, v) VALUES (0, 0) USING TIMESTAMP 0;", ConsistencyLevel.ALL, i);
Random random = new Random();
// CopyOnWriteArrayList<Throwable> exceptions = new CopyOnWriteArrayList<>();
final Semaphore inFlight = new Semaphore(concurrency);
final RateLimiter rateLimiter = RateLimiter.create(ratePerSecond);
long testStart = System.nanoTime();
// while (NANOSECONDS.toMinutes(System.nanoTime() - testStart) < 10 && exceptions.size() < 10000)
while (true)
{
final EstimatedHistogram histogram = new EstimatedHistogram(200);
long batchStart = System.nanoTime();
for (int i = 0 ; i < batchSize ; ++i)
{
inFlight.acquire();
rateLimiter.acquire();
long commandStart = System.nanoTime();
coordinator.executeWithResult((success, fail) -> {
inFlight.release();
if (fail == null) histogram.add(NANOSECONDS.toMicros(System.nanoTime() - commandStart));
// else exceptions.add(fail);
}, "UPDATE " + qualifiedTableName + " SET v += 1 WHERE k = ? IF EXISTS;", ConsistencyLevel.SERIAL, ConsistencyLevel.QUORUM, random.nextInt(keyCount));
}
System.out.printf("%tT rate: %.2f/s\n", new Date(), (((float)batchSize * 1000) / NANOSECONDS.toMillis(System.nanoTime() - batchStart)));
System.out.printf("%tT percentiles: %d %d %d %d\n", new Date(), histogram.percentile(.25)/1000, histogram.percentile(.5)/1000, histogram.percentile(.75)/1000, histogram.percentile(1)/1000);
}
}
);
}
@Override
protected Logger logger()
{
return logger;
}
}

View File

@ -38,7 +38,7 @@ import org.slf4j.LoggerFactory;
import accord.api.Key; import accord.api.Key;
import accord.api.Result; import accord.api.Result;
import accord.impl.CommandsForKey; import accord.local.CommandsForKey;
import accord.local.CheckedCommands; import accord.local.CheckedCommands;
import accord.local.Command; import accord.local.Command;
import accord.local.CommandStore; import accord.local.CommandStore;

View File

@ -31,8 +31,8 @@ import org.slf4j.LoggerFactory;
import accord.api.Key; import accord.api.Key;
import accord.api.Result; import accord.api.Result;
import accord.impl.CommandsForKey; import accord.local.CommandsForKey;
import accord.impl.CommandsForKeys; import accord.impl.TimestampsForKeys;
import accord.impl.TimestampsForKey; import accord.impl.TimestampsForKey;
import accord.local.Command; import accord.local.Command;
import accord.local.CommonAttributes; import accord.local.CommonAttributes;
@ -41,10 +41,13 @@ import accord.messages.Apply;
import accord.primitives.Ballot; import accord.primitives.Ballot;
import accord.primitives.PartialDeps; import accord.primitives.PartialDeps;
import accord.primitives.PartialTxn; import accord.primitives.PartialTxn;
import accord.primitives.Range;
import accord.primitives.Ranges; import accord.primitives.Ranges;
import accord.primitives.Routable;
import accord.primitives.Route; import accord.primitives.Route;
import accord.primitives.RoutingKeys; import accord.primitives.RoutingKeys;
import accord.primitives.Timestamp; import accord.primitives.Timestamp;
import accord.primitives.Txn;
import accord.primitives.TxnId; import accord.primitives.TxnId;
import accord.primitives.Writes; import accord.primitives.Writes;
import accord.utils.ImmutableBitSet; import accord.utils.ImmutableBitSet;
@ -102,20 +105,20 @@ public class AccordCommandStoreTest
AtomicLong clock = new AtomicLong(0); AtomicLong clock = new AtomicLong(0);
PartialTxn depTxn = createPartialTxn(0); PartialTxn depTxn = createPartialTxn(0);
Key key = (Key)depTxn.keys().get(0); Key key = (Key)depTxn.keys().get(0);
Range range = key.toUnseekable().asRange();
AccordCommandStore commandStore = createAccordCommandStore(clock::incrementAndGet, "ks", "tbl"); AccordCommandStore commandStore = createAccordCommandStore(clock::incrementAndGet, "ks", "tbl");
QueryProcessor.executeInternal("INSERT INTO ks.tbl (k, c, v) VALUES (0, 0, 1)"); QueryProcessor.executeInternal("INSERT INTO ks.tbl (k, c, v) VALUES (0, 0, 1)");
TableId tableId = Schema.instance.getTableMetadata("ks", "tbl").id; TableId tableId = Schema.instance.getTableMetadata("ks", "tbl").id;
TxnId oldTxnId1 = txnId(1, clock.incrementAndGet(), 1); TxnId oldTxnId1 = txnId(1, clock.incrementAndGet(), 1, Txn.Kind.Write, Routable.Domain.Range);
TxnId oldTxnId2 = txnId(1, clock.incrementAndGet(), 1); TxnId oldTxnId2 = txnId(1, clock.incrementAndGet(), 1, Txn.Kind.Write, Routable.Domain.Range);
TxnId oldTimestamp = txnId(1, clock.incrementAndGet(), 1); TxnId txnId = txnId(1, clock.incrementAndGet(), 1, Txn.Kind.Write, Routable.Domain.Range);
TxnId txnId = txnId(1, clock.incrementAndGet(), 1);
PartialDeps dependencies; PartialDeps dependencies;
try (PartialDeps.Builder builder = PartialDeps.builder(depTxn.covering())) try (PartialDeps.Builder builder = PartialDeps.builder(depTxn.covering()))
{ {
builder.add(key, oldTxnId1); builder.add(range, oldTxnId1);
builder.add(key, oldTxnId2); builder.add(range, oldTxnId2);
dependencies = builder.build(); dependencies = builder.build();
} }
@ -130,11 +133,9 @@ public class AccordCommandStoreTest
Ballot accepted = ballot(1, clock.incrementAndGet(), 1); Ballot accepted = ballot(1, clock.incrementAndGet(), 1);
Timestamp executeAt = timestamp(1, clock.incrementAndGet(), 1); Timestamp executeAt = timestamp(1, clock.incrementAndGet(), 1);
attrs.partialDeps(dependencies); attrs.partialDeps(dependencies);
SimpleBitSet waitingOnCommit = new SimpleBitSet(2); SimpleBitSet waitingOnApply = new SimpleBitSet(3);
waitingOnCommit.set(0);
SimpleBitSet waitingOnApply = new SimpleBitSet(2);
waitingOnApply.set(1); waitingOnApply.set(1);
Command.WaitingOn waitingOn = new Command.WaitingOn(dependencies, new ImmutableBitSet(waitingOnCommit), new ImmutableBitSet(waitingOnApply), new ImmutableBitSet(2)); Command.WaitingOn waitingOn = new Command.WaitingOn(dependencies.keyDeps.keys(), dependencies.rangeDeps.txnIds(), new ImmutableBitSet(waitingOnApply), new ImmutableBitSet(2));
attrs.addListener(new Command.ProxyListener(oldTxnId1)); attrs.addListener(new Command.ProxyListener(oldTxnId1));
Pair<Writes, Result> result = AccordTestUtils.processTxnResult(commandStore, txnId, txn, executeAt); Pair<Writes, Result> result = AccordTestUtils.processTxnResult(commandStore, txnId, txn, executeAt);
@ -183,10 +184,10 @@ public class AccordCommandStoreTest
AccordSafeTimestampsForKey tfk = new AccordSafeTimestampsForKey(loaded(key, null)); AccordSafeTimestampsForKey tfk = new AccordSafeTimestampsForKey(loaded(key, null));
tfk.initialize(); tfk.initialize();
CommandsForKeys.updateLastExecutionTimestamps(commandStore, tfk, txnId1, true); TimestampsForKeys.updateLastExecutionTimestamps(commandStore, tfk, txnId1, true);
Assert.assertEquals(txnId1.hlc(), AccordSafeTimestampsForKey.timestampMicrosFor(tfk.current(), txnId1, true)); Assert.assertEquals(txnId1.hlc(), AccordSafeTimestampsForKey.timestampMicrosFor(tfk.current(), txnId1, true));
CommandsForKeys.updateLastExecutionTimestamps(commandStore, tfk, txnId2, true); TimestampsForKeys.updateLastExecutionTimestamps(commandStore, tfk, txnId2, true);
Assert.assertEquals(txnId2.hlc(), AccordSafeTimestampsForKey.timestampMicrosFor(tfk.current(), txnId2, true)); Assert.assertEquals(txnId2.hlc(), AccordSafeTimestampsForKey.timestampMicrosFor(tfk.current(), txnId2, true));
Assert.assertEquals(txnId2, tfk.current().lastExecutedTimestamp()); Assert.assertEquals(txnId2, tfk.current().lastExecutedTimestamp());

View File

@ -26,7 +26,7 @@ import org.junit.Test;
import accord.api.Key; import accord.api.Key;
import accord.api.RoutingKey; import accord.api.RoutingKey;
import accord.impl.CommandsForKey; import accord.local.CommandsForKey;
import accord.local.Command; import accord.local.Command;
import accord.local.KeyHistory; import accord.local.KeyHistory;
import accord.local.Node; import accord.local.Node;
@ -117,7 +117,7 @@ public class AccordCommandTest
Assert.assertEquals(Status.PreAccepted, command.status()); Assert.assertEquals(Status.PreAccepted, command.status());
Assert.assertTrue(command.partialDeps() == null || command.partialDeps().isEmpty()); Assert.assertTrue(command.partialDeps() == null || command.partialDeps().isEmpty());
CommandsForKey cfk = ((AccordSafeCommandStore) instance).commandsForKey(key(1)).current(); CommandsForKey cfk = ((AccordSafeCommandStore) instance).get(key(1)).current();
Assert.assertTrue(cfk.indexOf(txnId) >= 0); Assert.assertTrue(cfk.indexOf(txnId) >= 0);
})); }));
@ -145,7 +145,7 @@ public class AccordCommandTest
Assert.assertEquals(Status.Accepted, command.status()); Assert.assertEquals(Status.Accepted, command.status());
Assert.assertEquals(deps, command.partialDeps()); Assert.assertEquals(deps, command.partialDeps());
CommandsForKey cfk = ((AccordSafeCommandStore) instance).commandsForKey(key(1)).current(); CommandsForKey cfk = ((AccordSafeCommandStore) instance).get(key(1)).current();
Assert.assertTrue(cfk.indexOf(txnId) >= 0); Assert.assertTrue(cfk.indexOf(txnId) >= 0);
})); }));
@ -160,7 +160,7 @@ public class AccordCommandTest
Assert.assertTrue(command.hasBeen(Status.Committed)); Assert.assertTrue(command.hasBeen(Status.Committed));
Assert.assertEquals(commit.partialDeps, command.partialDeps()); Assert.assertEquals(commit.partialDeps, command.partialDeps());
CommandsForKey cfk = ((AccordSafeCommandStore) instance).commandsForKey(key(1)).current(); CommandsForKey cfk = ((AccordSafeCommandStore) instance).get(key(1)).current();
Assert.assertTrue(cfk.indexOf(txnId) >= 0); Assert.assertTrue(cfk.indexOf(txnId) >= 0);
})); }));
} }

View File

@ -30,6 +30,8 @@ import java.util.function.LongSupplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import javax.annotation.Nullable;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import org.junit.Assert; import org.junit.Assert;
@ -59,6 +61,7 @@ import accord.primitives.PartialDeps;
import accord.primitives.PartialTxn; import accord.primitives.PartialTxn;
import accord.primitives.Participants; import accord.primitives.Participants;
import accord.primitives.Ranges; import accord.primitives.Ranges;
import accord.primitives.Routable;
import accord.primitives.Route; import accord.primitives.Route;
import accord.primitives.Seekable; import accord.primitives.Seekable;
import accord.primitives.Seekables; import accord.primitives.Seekables;
@ -199,8 +202,8 @@ public class AccordTestUtils
@Override public void executed(Command command, ProgressShard progressShard) {} @Override public void executed(Command command, ProgressShard progressShard) {}
@Override public void clear(TxnId txnId) {} @Override public void clear(TxnId txnId) {}
@Override public void durable(Command command) {} @Override public void durable(Command command) {}
@Override @Override public void waiting(SafeCommand blockedBy, LocalExecution blockedUntil, Route<?> blockedOnRoute, Participants<?> blockedOnParticipants) {}
public void waiting(SafeCommand blockedBy, LocalExecution blockedUntil, Route<?> blockedOnRoute, Participants<?> blockedOnParticipants) {} @Override public void waiting(TxnId blockedBy, LocalExecution blockedUntil, @Nullable Route<?> blockedOnRoute, @Nullable Participants<?> blockedOnParticipants) {}
}; };
public static TxnId txnId(long epoch, long hlc, int node) public static TxnId txnId(long epoch, long hlc, int node)
@ -213,6 +216,11 @@ public class AccordTestUtils
return new TxnId(epoch, hlc, kind, Key, new Node.Id(node)); return new TxnId(epoch, hlc, kind, Key, new Node.Id(node));
} }
public static TxnId txnId(long epoch, long hlc, int node, Txn.Kind kind, Routable.Domain domain)
{
return new TxnId(epoch, hlc, kind, domain, new Node.Id(node));
}
public static Timestamp timestamp(long epoch, long hlc, int node) public static Timestamp timestamp(long epoch, long hlc, int node)
{ {
return Timestamp.fromValues(epoch, hlc, new Node.Id(node)); return Timestamp.fromValues(epoch, hlc, new Node.Id(node));

View File

@ -32,13 +32,12 @@ import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import accord.api.Key; import accord.api.Key;
import accord.impl.CommandsForKey; import accord.local.CommandsForKey;
import accord.impl.TimestampsForKey; import accord.impl.TimestampsForKey;
import accord.local.Command; import accord.local.Command;
import accord.local.KeyHistory; import accord.local.KeyHistory;
import accord.primitives.Keys; import accord.primitives.Keys;
import accord.primitives.PartialTxn; import accord.primitives.PartialTxn;
import accord.primitives.RoutableKey;
import accord.primitives.TxnId; import accord.primitives.TxnId;
import accord.utils.async.AsyncChains; import accord.utils.async.AsyncChains;
import accord.utils.async.AsyncResult; import accord.utils.async.AsyncResult;
@ -96,7 +95,7 @@ public class AsyncLoaderTest
AccordStateCache.Instance<TxnId, Command, AccordSafeCommand> commandCache = commandStore.commandCache(); AccordStateCache.Instance<TxnId, Command, AccordSafeCommand> commandCache = commandStore.commandCache();
commandStore.executeBlocking(() -> commandStore.setCapacity(1024)); commandStore.executeBlocking(() -> commandStore.setCapacity(1024));
AccordStateCache.Instance<RoutableKey, TimestampsForKey, AccordSafeTimestampsForKey> timestampsCache = commandStore.timestampsForKeyCache(); AccordStateCache.Instance<Key, TimestampsForKey, AccordSafeTimestampsForKey> timestampsCache = commandStore.timestampsForKeyCache();
TxnId txnId = txnId(1, clock.incrementAndGet(), 1); TxnId txnId = txnId(1, clock.incrementAndGet(), 1);
PartialTxn txn = createPartialTxn(0); PartialTxn txn = createPartialTxn(0);
PartitionKey key = (PartitionKey) Iterables.getOnlyElement(txn.keys()); PartitionKey key = (PartitionKey) Iterables.getOnlyElement(txn.keys());
@ -375,7 +374,7 @@ public class AsyncLoaderTest
@Test @Test
public void inProgressCFKSaveTest() public void inProgressCFKSaveTest()
{ {
inProgressCFKSaveTest(COMMANDS, AccordCommandStore::commandsForKeyCache, context -> context.commandsForKey, CommandsForKey::new, (cfk, u) -> cfk.update(null, u)); this.inProgressCFKSaveTest(COMMANDS, AccordCommandStore::commandsForKeyCache, context -> context.commandsForKey, CommandsForKey::new, (cfk, u) -> cfk.update(null, u));
} }
@Test @Test
@ -384,7 +383,7 @@ public class AsyncLoaderTest
inProgressCFKSaveTest(TIMESTAMPS, AccordCommandStore::timestampsForKeyCache, context -> context.timestampsForKey, TimestampsForKey::new, (tfk, c) -> new TimestampsForKey(tfk.key(), c.executeAt(), c.executeAt().hlc(), c.executeAt())); inProgressCFKSaveTest(TIMESTAMPS, AccordCommandStore::timestampsForKeyCache, context -> context.timestampsForKey, TimestampsForKey::new, (tfk, c) -> new TimestampsForKey(tfk.key(), c.executeAt(), c.executeAt().hlc(), c.executeAt()));
} }
private <T1, T2 extends AccordSafeState<RoutableKey, T1>, C extends AccordStateCache.Instance<RoutableKey, T1, T2>> void inProgressCFKSaveTest(KeyHistory history, Function<AccordCommandStore, C> getter, Function<Context, TreeMap<?, ?>> inContext, Function<Key, T1> initialiser, BiFunction<T1, Command, T1> update) private <T1, T2 extends AccordSafeState<Key, T1>, C extends AccordStateCache.Instance<Key, T1, T2>> void inProgressCFKSaveTest(KeyHistory history, Function<AccordCommandStore, C> getter, Function<Context, TreeMap<?, ?>> inContext, Function<Key, T1> initialiser, BiFunction<T1, Command, T1> update)
{ {
AtomicLong clock = new AtomicLong(0); AtomicLong clock = new AtomicLong(0);
ManualExecutor executor = new ManualExecutor(); ManualExecutor executor = new ManualExecutor();

View File

@ -36,7 +36,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import accord.api.RoutingKey; import accord.api.RoutingKey;
import accord.impl.SafeCommandsForKey; import accord.local.SafeCommandsForKey;
import accord.local.CheckedCommands; import accord.local.CheckedCommands;
import accord.local.Command; import accord.local.Command;
import accord.local.PreLoadContext; import accord.local.PreLoadContext;
@ -53,7 +53,6 @@ import accord.primitives.PartialDeps;
import accord.primitives.PartialRoute; import accord.primitives.PartialRoute;
import accord.primitives.PartialTxn; import accord.primitives.PartialTxn;
import accord.primitives.Ranges; import accord.primitives.Ranges;
import accord.primitives.RoutableKey;
import accord.primitives.Route; import accord.primitives.Route;
import accord.primitives.Timestamp; import accord.primitives.Timestamp;
import accord.primitives.Txn; import accord.primitives.Txn;
@ -259,7 +258,7 @@ public class AsyncOperationTest
Accept accept = Accept accept =
Accept.SerializerSupport.create(txnId, partialRoute, txnId.epoch(), txnId.epoch(), false, Ballot.ZERO, executeAt, partialTxn.keys(), deps); Accept.SerializerSupport.create(txnId, partialRoute, txnId.epoch(), txnId.epoch(), false, Ballot.ZERO, executeAt, partialTxn.keys(), deps);
Commit commit = Commit commit =
Commit.SerializerSupport.create(txnId, partialRoute, txnId.epoch(), Commit.Kind.Commit, Ballot.ZERO, executeAt, partialTxn.keys(), partialTxn, deps, route, null); Commit.SerializerSupport.create(txnId, partialRoute, txnId.epoch(), Commit.Kind.CommitSlowPath, Ballot.ZERO, executeAt, partialTxn.keys(), partialTxn, deps, route, null);
Commit stable = Commit stable =
Commit.SerializerSupport.create(txnId, partialRoute, txnId.epoch(), Commit.Kind.StableSlowPath, Ballot.ZERO, executeAt, partialTxn.keys(), partialTxn, deps, route, null); Commit.SerializerSupport.create(txnId, partialRoute, txnId.epoch(), Commit.Kind.StableSlowPath, Ballot.ZERO, executeAt, partialTxn.keys(), partialTxn, deps, route, null);
@ -482,8 +481,7 @@ public class AsyncOperationTest
} }
try try
{ {
//TODO this is due to bad typing for Instance, it doesn't use ? extends RoutableKey assertNoReferences(commandStore.commandsForKeyCache(), keys);
assertNoReferences(commandStore.commandsForKeyCache(), (Iterable<RoutableKey>) (Iterable<?>) keys);
} }
catch (AssertionError e) catch (AssertionError e)
{ {
@ -524,8 +522,7 @@ public class AsyncOperationTest
private static void awaitDone(AccordCommandStore commandStore, List<TxnId> ids, Keys keys) private static void awaitDone(AccordCommandStore commandStore, List<TxnId> ids, Keys keys)
{ {
awaitDone(commandStore.commandCache(), ids); awaitDone(commandStore.commandCache(), ids);
//TODO this is due to bad typing for Instance, it doesn't use ? extends RoutableKey awaitDone(commandStore.commandsForKeyCache(), keys);
awaitDone(commandStore.commandsForKeyCache(), (Iterable<RoutableKey>) (Iterable<?>) keys);
} }
private static <T> void awaitDone(AccordStateCache.Instance<T, ?, ?> cache, Iterable<T> keys) private static <T> void awaitDone(AccordStateCache.Instance<T, ?, ?> cache, Iterable<T> keys)

View File

@ -38,9 +38,10 @@ import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import accord.api.Key; import accord.api.Key;
import accord.impl.CommandsForKey; import accord.local.CommandsForKey;
import accord.impl.CommandsForKey.InternalStatus; import accord.local.CommandsForKey.InternalStatus;
import accord.local.Command; import accord.local.Command;
import accord.local.CommandsForKey.TxnInfo;
import accord.local.CommonAttributes; import accord.local.CommonAttributes;
import accord.local.CommonAttributes.Mutable; import accord.local.CommonAttributes.Mutable;
import accord.local.Listeners; import accord.local.Listeners;
@ -241,9 +242,12 @@ public class CommandsForKeySerializerTest
List<TxnId> deps = cmds[i].deps; List<TxnId> deps = cmds[i].deps;
List<TxnId> missing = cmds[i].missing; List<TxnId> missing = cmds[i].missing;
for (int j = 0 ; j < limit ; ++j) for (int j = 0 ; j < limit ; ++j)
if (i != j) deps.add(cmds[j].txnId); {
if (i != j && cmds[i].txnId.kind().witnesses(cmds[j].txnId))
deps.add(cmds[j].txnId);
}
int missingCount = Math.min(limit - (limit > i ? 1 : 0), missingCountSupplier.getAsInt()); int missingCount = Math.min(deps.size(), missingCountSupplier.getAsInt());
while (missingCount > 0) while (missingCount > 0)
{ {
int remove = source.nextInt(deps.size()); int remove = source.nextInt(deps.size());
@ -267,14 +271,14 @@ public class CommandsForKeySerializerTest
{ {
InternalStatus status = InternalStatus.from(cmds[j].saveStatus); InternalStatus status = InternalStatus.from(cmds[j].saveStatus);
if (status == null || !status.hasInfo) continue; if (status == null || !status.hasInfo) continue;
if (status.depsKnownBefore(cmds[j].txnId, cmds[j].executeAt).compareTo(cmds[i].txnId) > 0 && Collections.binarySearch(cmds[j].missing, cmds[i].txnId) < 0) if (cmds[j].txnId.kind().witnesses(cmds[i].txnId) && status.depsKnownBefore(cmds[j].txnId, cmds[j].executeAt).compareTo(cmds[i].txnId) > 0 && Collections.binarySearch(cmds[j].missing, cmds[i].txnId) < 0)
continue outer; continue outer;
} }
for (int j = i + 1 ; j < cmds.length ; ++j) for (int j = i + 1 ; j < cmds.length ; ++j)
{ {
InternalStatus status = InternalStatus.from(cmds[j].saveStatus); InternalStatus status = InternalStatus.from(cmds[j].saveStatus);
if (status == null || !status.hasInfo) continue; if (status == null || !status.hasInfo) continue;
if (Collections.binarySearch(cmds[j].missing, cmds[i].txnId) < 0) if (cmds[j].txnId.kind().witnesses(cmds[i].txnId) && Collections.binarySearch(cmds[j].missing, cmds[i].txnId) < 0)
continue outer; continue outer;
} }
cmds[i].invisible = true; cmds[i].invisible = true;
@ -322,7 +326,7 @@ public class CommandsForKeySerializerTest
@Test @Test
public void serde() public void serde()
{ {
// testOne(1821931462020409370L); testOne(-6946067792202944553L);
Random random = new Random(); Random random = new Random();
for (int i = 0 ; i < 10000 ; ++i) for (int i = 0 ; i < 10000 ; ++i)
{ {
@ -426,13 +430,13 @@ public class CommandsForKeySerializerTest
Assert.assertTrue(cmd.invisible); Assert.assertTrue(cmd.invisible);
continue; continue;
} }
CommandsForKey.Info info = cfk.info(i); TxnInfo info = cfk.get(i);
InternalStatus expectStatus = InternalStatus.from(cmd.saveStatus); InternalStatus expectStatus = InternalStatus.from(cmd.saveStatus);
if (expectStatus == null) expectStatus = InternalStatus.TRANSITIVELY_KNOWN; if (expectStatus == null) expectStatus = InternalStatus.TRANSITIVELY_KNOWN;
if (expectStatus.hasInfo) if (expectStatus.hasInfo)
Assert.assertEquals(cmd.executeAt, info.executeAt(cfk.txnId(i))); Assert.assertEquals(cmd.executeAt, info.executeAt);
Assert.assertEquals(expectStatus, info.status); Assert.assertEquals(expectStatus, info.status);
Assert.assertArrayEquals(cmd.missing.toArray(TxnId[]::new), info.missing); Assert.assertArrayEquals(cmd.missing.toArray(TxnId[]::new), info.missing());
++i; ++i;
} }
@ -461,11 +465,12 @@ public class CommandsForKeySerializerTest
next = txnIdGen.next(rs0); next = txnIdGen.next(rs0);
return next; return next;
}).unique().ofSizeBetween(0, 10).next(rs); }).unique().ofSizeBetween(0, 10).next(rs);
CommandsForKey.Info[] info = new CommandsForKey.Info[ids.length]; TxnInfo[] info = new TxnInfo[ids.length];
for (int i = 0; i < info.length; i++) for (int i = 0; i < info.length; i++)
info[i] = rs.pick(InternalStatus.values()).asNoInfo; info[i] = TxnInfo.create(ids[i], rs.pick(InternalStatus.values()), ids[i], CommandsForKey.NO_TXNIDS);
Arrays.sort(ids, Comparator.naturalOrder()); Arrays.sort(info, Comparator.naturalOrder());
CommandsForKey expected = CommandsForKey.SerializerSupport.create(pk, redudentBefore, ids, info);
CommandsForKey expected = CommandsForKey.SerializerSupport.create(pk, info, CommandsForKey.NO_PENDING_UNMANAGED);
ByteBuffer buffer = CommandsForKeySerializer.toBytesWithoutKey(expected); ByteBuffer buffer = CommandsForKeySerializer.toBytesWithoutKey(expected);
CommandsForKey roundTrip = CommandsForKeySerializer.fromBytes(pk, buffer); CommandsForKey roundTrip = CommandsForKeySerializer.fromBytes(pk, buffer);
@ -479,10 +484,10 @@ public class CommandsForKeySerializerTest
long tokenValue = -2311778975040348869L; long tokenValue = -2311778975040348869L;
DecoratedKey key = Murmur3Partitioner.instance.decorateKey(Murmur3Partitioner.LongToken.keyForToken(tokenValue)); DecoratedKey key = Murmur3Partitioner.instance.decorateKey(Murmur3Partitioner.LongToken.keyForToken(tokenValue));
PartitionKey pk = new PartitionKey(TableId.fromString("1b255f4d-ef25-40a6-0000-000000000009"), key); PartitionKey pk = new PartitionKey(TableId.fromString("1b255f4d-ef25-40a6-0000-000000000009"), key);
TxnId txnId = TxnId.fromValues(11,34052499,2,1);
CommandsForKey expected = CommandsForKey.SerializerSupport.create(pk, CommandsForKey expected = CommandsForKey.SerializerSupport.create(pk,
TxnId.fromValues(0,0,0,0), new TxnInfo[] { TxnInfo.create(txnId, InternalStatus.PREACCEPTED, txnId, CommandsForKey.NO_TXNIDS) },
new TxnId[] {TxnId.fromValues(11,34052499,2,1)}, CommandsForKey.NO_PENDING_UNMANAGED);
new CommandsForKey.Info[] { InternalStatus.PREACCEPTED.asNoInfo});
ByteBuffer buffer = CommandsForKeySerializer.toBytesWithoutKey(expected); ByteBuffer buffer = CommandsForKeySerializer.toBytesWithoutKey(expected);
CommandsForKey roundTrip = CommandsForKeySerializer.fromBytes(pk, buffer); CommandsForKey roundTrip = CommandsForKeySerializer.fromBytes(pk, buffer);

View File

@ -23,6 +23,8 @@ import org.junit.Test;
import accord.local.Command; import accord.local.Command;
import accord.primitives.Deps; import accord.primitives.Deps;
import accord.primitives.Routable;
import accord.primitives.TxnId;
import accord.utils.Gen; import accord.utils.Gen;
import accord.utils.Gens; import accord.utils.Gens;
import accord.utils.SimpleBitSet; import accord.utils.SimpleBitSet;
@ -51,19 +53,20 @@ public class WaitingOnSerializerTest
{ {
DataOutputBuffer buffer = new DataOutputBuffer(); DataOutputBuffer buffer = new DataOutputBuffer();
qt().forAll(waitingOnGen()).check(waitingOn -> { qt().forAll(waitingOnGen()).check(waitingOn -> {
TxnId txnId = TxnId.NONE;
if (waitingOn.appliedOrInvalidated != null) txnId = new TxnId(txnId.epoch(), txnId.hlc(), txnId.kind(), Routable.Domain.Range, txnId.node);
buffer.clear(); buffer.clear();
long expectedSize = WaitingOnSerializer.serializedSize(waitingOn); long expectedSize = WaitingOnSerializer.serializedSize(waitingOn);
WaitingOnSerializer.serialize(waitingOn, buffer); WaitingOnSerializer.serialize(txnId, waitingOn, buffer);
Assertions.assertThat(buffer.getLength()).isEqualTo(expectedSize); Assertions.assertThat(buffer.getLength()).isEqualTo(expectedSize);
Command.WaitingOn read = WaitingOnSerializer.deserialize(waitingOn.deps, new DataInputBuffer(buffer.unsafeGetBufferAndFlip(), false)); Command.WaitingOn read = WaitingOnSerializer.deserialize(txnId, waitingOn.keys, waitingOn.txnIds, new DataInputBuffer(buffer.unsafeGetBufferAndFlip(), false));
Assertions.assertThat(read) Assertions.assertThat(read)
.isEqualTo(waitingOn) .isEqualTo(waitingOn)
.isEqualTo(WaitingOnSerializer.deserialize(waitingOn.deps, WaitingOnSerializer.serialize(waitingOn))); .isEqualTo(WaitingOnSerializer.deserialize(txnId, waitingOn.keys, waitingOn.txnIds, WaitingOnSerializer.serialize(txnId, waitingOn)));
}); });
} }
private enum WaitingOnSets private enum WaitingOnSets { APPLY, APPLIED_OR_INVALIDATED }
{COMMIT, APPLY, APPLYED_OR_INVALIDATED}
private static Gen<Command.WaitingOn> waitingOnGen() private static Gen<Command.WaitingOn> waitingOnGen()
{ {
@ -73,22 +76,20 @@ public class WaitingOnSerializerTest
return rs -> { return rs -> {
Deps deps = depsGen.next(rs); Deps deps = depsGen.next(rs);
if (deps.isEmpty()) return Command.WaitingOn.EMPTY; if (deps.isEmpty()) return Command.WaitingOn.EMPTY;
int[] selected = Gens.arrays(Gens.ints().between(0, deps.txnIdCount() - 1)).unique().ofSizeBetween(0, deps.txnIdCount() - 1).next(rs); int txnIdCount = deps.rangeDeps.txnIdCount();
SimpleBitSet waitingOnCommit = new SimpleBitSet(deps.txnIdCount(), false); int keyCount = deps.keyDeps.keys().size();
SimpleBitSet waitingOnApply = new SimpleBitSet(deps.txnIdCount(), false); int[] selected = Gens.arrays(Gens.ints().between(0, txnIdCount + keyCount - 1)).unique().ofSizeBetween(0, txnIdCount + keyCount).next(rs);
SimpleBitSet appliedOrInvalidated = new SimpleBitSet(deps.txnIdCount(), false); SimpleBitSet waitingOn = new SimpleBitSet(txnIdCount + keyCount, false);
SimpleBitSet appliedOrInvalidated = rs.nextBoolean() ? null : new SimpleBitSet(txnIdCount, false);
for (int i : selected) for (int i : selected)
{ {
WaitingOnSets set = sets.next(rs); WaitingOnSets set = appliedOrInvalidated == null || i >= txnIdCount ? WaitingOnSets.APPLY : sets.next(rs);
switch (set) switch (set)
{ {
case COMMIT:
waitingOnCommit.set(i);
break;
case APPLY: case APPLY:
waitingOnApply.set(i); waitingOn.set(i);
break; break;
case APPLYED_OR_INVALIDATED: case APPLIED_OR_INVALIDATED:
appliedOrInvalidated.set(i); appliedOrInvalidated.set(i);
break; break;
default: default:
@ -96,7 +97,7 @@ public class WaitingOnSerializerTest
} }
} }
return new Command.WaitingOn(deps, Utils.ensureImmutable(waitingOnCommit), Utils.ensureImmutable(waitingOnApply), Utils.ensureImmutable(appliedOrInvalidated)); return new Command.WaitingOn(deps.keyDeps.keys(), deps.rangeDeps.txnIds(), Utils.ensureImmutable(waitingOn), Utils.ensureImmutable(appliedOrInvalidated));
}; };
} }
} }