CASSANDRA-18519: (CEP-15: (C*) Add notion of CommandsForRanges and make this durable in C*)#46
CASSANDRA-18519: (CEP-15: (C*) Add notion of CommandsForRanges and make this durable in C*)#46dcapwell wants to merge 38 commits intoapache:trunkfrom
Conversation
| import static accord.utils.Utils.ensureSortedImmutable; | ||
| import static accord.utils.Utils.ensureSortedMutable; | ||
|
|
||
| public class CommandTimeseries<D> |
There was a problem hiding this comment.
just moved this out of CommandsForKeys so that the range logic didn't have to duplicate anything... I just build this time series from the data on-demand
| public enum TestTimestamp | ||
| {BEFORE, AFTER} | ||
|
|
||
| private final Seekable keyOrRange; |
There was a problem hiding this comment.
this type change is the only real change to this class
|
|
||
| package accord.impl; | ||
|
|
||
| public interface CommandTimeseriesHolder |
There was a problem hiding this comment.
not a fan of the name, so any recommendations welcome =)
…. This is to make way for range txn support
| } | ||
|
|
||
| private <P1> int[] subsetFor(Unseekables<?, ?> select, IndexedPredicate<P1> predicate, P1 param) | ||
| private enum OnUnknown { REJECT, IGNORE } |
There was a problem hiding this comment.
this was actually a hack to make progress...
What I saw was that the local epoch=1, but we saw epoch=2, we try to see if there is overlap and fail as epoch=1 doesn't have the new table range that is defined in epoch=2
I changed C* code to defer exec of messages and PreLoadContext until the epoch they depend on is ready... which may remove the need for this hack...
This likely hasn't been found in burn due to working with a single range, so only would impact new ranges being added and used right away (which happens in C* tests)
There was a problem hiding this comment.
It kind of makes sense as a work around for the case you are describing, where is this code that tries to compare different epochs that fails?
It seems like this change is opting everything into the IGNORE behavior?
There was a problem hiding this comment.
Went with ignore for all cases as I couldn’t find any caller that allowed the failure. At the dr office so can’t show, it changing default and running AccordCQLTest you hit this right away
There was a problem hiding this comment.
here is the exception
java.lang.IllegalArgumentException: Range not found for TokenKey{keyspace=demo_ks, key=-9079300900759362424}
at accord.topology.Topology.subsetFor(Topology.java:266)
at accord.topology.Topology.visitNodeForKeysOnceOrMore(Topology.java:337)
at accord.topology.TopologyManager.withUnsyncedEpochs(TopologyManager.java:341)
at accord.topology.TopologyManager.withUnsyncedEpochs(TopologyManager.java:310)
at accord.coordinate.CoordinatePreAccept.<init>(CoordinatePreAccept.java:129)
at accord.coordinate.CoordinateTransaction.<init>(CoordinateTransaction.java:49)
at accord.coordinate.CoordinateTransaction.coordinate(CoordinateTransaction.java:54)
at accord.local.Node.initiateCoordination(Node.java:447)
at accord.local.Node.lambda$coordinate$9(Node.java:439)
at accord.local.Node.withEpoch(Node.java:220)
at accord.local.Node.coordinate(Node.java:439)
So, the use case is the following
- create a new Keyspace w/ table
- run a txn over that new table
The org.apache.cassandra.distributed.test.accord.NewSchemaTest test stresses this edge case to make sure the different epochs are supported
There was a problem hiding this comment.
Huh, interesting consequence of having the table id in the MSB of the key.
There was a problem hiding this comment.
in this case, TokenKey is scoped to the keyspace, so a new table in an existing keyspace is fine, but adding a new keyspace and touching it causes this.
There was a problem hiding this comment.
Ah right, I was concerned about this because it causes false conflicts across tables in a key space. Discussed it with Benedict and we might want to scope it differently at some point.
| import accord.primitives.Ranges; | ||
| import accord.primitives.TxnId; | ||
|
|
||
| public class RangeUnavailable extends Exhausted |
There was a problem hiding this comment.
Exhausted could be different things, so this makes it easier to see what happened; what table ranges could not be read?
|
|
||
| for (D data : (testTimestamp == TestTimestamp.BEFORE ? commands.headMap(timestamp, false) : commands.tailMap(timestamp, false)).values()) | ||
| { | ||
| TxnId txnId = loader.txnId(data); |
There was a problem hiding this comment.
I changed the ordering of this method due to cost to deserialize for CommandsForKeys, idea was to only deserialize what is needed and avoid touching deps until needed
aweisberg
left a comment
There was a problem hiding this comment.
Nothing major, some questions. The Topology change is the main thing I need to understand better since it is always opting into IGNORE at the moment.
| } | ||
|
|
||
| private <P1> int[] subsetFor(Unseekables<?, ?> select, IndexedPredicate<P1> predicate, P1 param) | ||
| private enum OnUnknown { REJECT, IGNORE } |
There was a problem hiding this comment.
It kind of makes sense as a work around for the case you are describing, where is this code that tries to compare different epochs that fails?
It seems like this change is opting everything into the IGNORE behavior?
| return construct(cachedRanges.completeAndDiscard(result, count)); | ||
| } | ||
|
|
||
| public Map<Boolean, Ranges> partitioningBy(Predicate<? super Range> test) |
There was a problem hiding this comment.
moved away from the Partition class as this actually is better the more I looked at it... Partition class didn't properly handle this.isEmpty(), but with a map we have a few states
- map is empty
- map(true)
- map(false)
- map(true, false)
With this change, we at least have the same signature as Collectors.partitioningBy
| Runner.run(nodeCount, new StandardQueue.Factory(randomSource), randomSource::fork, factory, commands); | ||
| try | ||
| { | ||
| Runner.run(nodeCount, new StandardQueue.Factory(randomSource), randomSource::fork, factory, commands); |
There was a problem hiding this comment.
these tests are not deterministic, so same seed doesn't actually get the same results =(
| } | ||
|
|
||
| @Override | ||
| protected void registerHistoricalTransactions(Deps deps) |
There was a problem hiding this comment.
moved this to safe command store... we can't handle this in CommandStore for C* unless we lazy load the dependencies (which requires #27)
| NodeTimeService time(); | ||
| CommandStores.RangesForEpoch ranges(); | ||
| Timestamp maxConflict(Seekables<?, ?> keys, Ranges slice); | ||
| void registerHistoricalTransactions(Deps deps); |
There was a problem hiding this comment.
I had intended to keep this to CommandStore to isolate the APIs - SafeCommandStore is for working with Command and SafeCommand whereas CommandStore is for internal book-keeping operations.
There was a problem hiding this comment.
sorry, just saw this after merge...
In order to make the outcome of this method durable, it has to be in the safe store for C*, so felt it was best to move it there as that's were we mutate state normally
| case Invalidated: | ||
| return true; | ||
| default: | ||
| return false; |
There was a problem hiding this comment.
I would prefer not to use default properties, as it makes it harder to safely modify the state machine in future.
There was a problem hiding this comment.
Is this actually used?
There was a problem hiding this comment.
it was, but then deleted in favor of something else; but forgot to purge this
No description provided.