Skip to content

Commit 758a2bc

Browse files
committed
Fix null field accounting
Patch by Alex Petrov; reviewed by Benedict Elliott Smith for CASSANDRA-20297
1 parent 9328b83 commit 758a2bc

File tree

2 files changed

+53
-112
lines changed

2 files changed

+53
-112
lines changed

accord-core/src/main/java/accord/impl/CommandChange.java

Lines changed: 40 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,13 @@
5252
import static accord.impl.CommandChange.Field.EXECUTES_AT_LEAST;
5353
import static accord.impl.CommandChange.Field.EXECUTE_AT;
5454
import static accord.impl.CommandChange.Field.FIELDS;
55+
import static accord.impl.CommandChange.Field.MIN_UNIQUE_HLC;
5556
import static accord.impl.CommandChange.Field.PARTIAL_DEPS;
5657
import static accord.impl.CommandChange.Field.PARTIAL_TXN;
5758
import static accord.impl.CommandChange.Field.PARTICIPANTS;
5859
import static accord.impl.CommandChange.Field.PROMISED;
5960
import static accord.impl.CommandChange.Field.RESULT;
6061
import static accord.impl.CommandChange.Field.SAVE_STATUS;
61-
import static accord.impl.CommandChange.Field.MIN_UNIQUE_HLC;
6262
import static accord.impl.CommandChange.Field.WAITING_ON;
6363
import static accord.impl.CommandChange.Field.WRITES;
6464
import static accord.local.Cleanup.NO;
@@ -74,7 +74,11 @@
7474
import static accord.local.Command.Truncated.invalidated;
7575
import static accord.local.Command.Truncated.vestigial;
7676
import static accord.local.StoreParticipants.Filter.LOAD;
77+
import static accord.primitives.Known.Definition.DefinitionErased;
78+
import static accord.primitives.Known.KnownDeps.DepsErased;
7779
import static accord.primitives.Known.KnownExecuteAt.ApplyAtKnown;
80+
import static accord.primitives.Known.KnownExecuteAt.ExecuteAtErased;
81+
import static accord.primitives.Known.Outcome.WasApply;
7882
import static accord.primitives.Status.Durability.NotDurable;
7983

8084
public class CommandChange
@@ -101,6 +105,32 @@ public enum Field
101105
public static final Field[] FIELDS = values();
102106
}
103107

108+
/**
109+
* SaveStatus.Known contains information about erased / nullified fields,
110+
* which we can use in order to mark the corresponding fields as changed
111+
* and setting them to null when they are erased.
112+
*/
113+
public static int[] saveStatusMasks;
114+
115+
static
116+
{
117+
saveStatusMasks = new int[SaveStatus.values().length];
118+
for (int i = 0; i < saveStatusMasks.length; i++)
119+
{
120+
SaveStatus saveStatus = SaveStatus.forOrdinal(i);
121+
int mask = 0;
122+
if (saveStatus.known.deps() == DepsErased)
123+
mask |= setFieldIsNullAndChanged(PARTIAL_DEPS, mask);
124+
if (saveStatus.known.executeAt() == ExecuteAtErased)
125+
mask |= setFieldIsNullAndChanged(EXECUTE_AT, mask);
126+
if (saveStatus.known.definition() == DefinitionErased)
127+
mask |= setFieldIsNullAndChanged(PARTIAL_TXN, mask);
128+
if (saveStatus.known.outcome() == WasApply)
129+
mask |= setFieldIsNullAndChanged(RESULT, mask);
130+
saveStatusMasks[i] = mask;
131+
}
132+
}
133+
104134
public static class Builder
105135
{
106136
protected final int mask;
@@ -151,72 +181,16 @@ public Builder()
151181
this(ALL);
152182
}
153183

154-
public TxnId txnId()
155-
{
156-
return txnId;
157-
}
158-
159-
public Timestamp executeAt()
160-
{
161-
return executeAt;
162-
}
163-
164-
// TODO: why is this unused in BurnTest
165-
public Timestamp executeAtLeast()
166-
{
167-
return executeAtLeast;
168-
}
169-
170184
public SaveStatus saveStatus()
171185
{
172186
return saveStatus;
173187
}
174188

175-
public Status.Durability durability()
176-
{
177-
return durability;
178-
}
179-
180-
public Ballot acceptedOrCommitted()
181-
{
182-
return acceptedOrCommitted;
183-
}
184-
185-
public Ballot promised()
186-
{
187-
return promised;
188-
}
189-
190189
public StoreParticipants participants()
191190
{
192191
return participants;
193192
}
194193

195-
public PartialTxn partialTxn()
196-
{
197-
return partialTxn;
198-
}
199-
200-
public PartialDeps partialDeps()
201-
{
202-
return partialDeps;
203-
}
204-
205-
public CommandChange.WaitingOnProvider waitingOn()
206-
{
207-
return waitingOn;
208-
}
209-
210-
public Writes writes()
211-
{
212-
return writes;
213-
}
214-
215-
public Result result()
216-
{
217-
return result;
218-
}
219-
220194
public void clear()
221195
{
222196
flags = 0;
@@ -286,7 +260,7 @@ public Cleanup shouldCleanup(Input input, Agent agent, RedundantBefore redundant
286260

287261
public Builder maybeCleanup(Cleanup cleanup)
288262
{
289-
if (saveStatus() == null)
263+
if (saveStatus == null)
290264
return this;
291265

292266
switch (cleanup)
@@ -506,6 +480,10 @@ public static int getFlags(Command before, Command after)
506480
if (before == null && after == null)
507481
return flags;
508482

483+
// TODO (desired): as of now, we don't declare that any of the fields as Erased, even though we have erased them.
484+
if (before == null && after.saveStatus() == SaveStatus.Vestigial)
485+
before = Command.NotDefined.uninitialised(after.txnId());
486+
509487
// TODO (expected): derive this from precomputed bit masking on Known, only testing equality of objects we can't infer directly
510488
flags = collectFlags(before, after, Command::executeAt, Timestamp::equalsStrict, true, EXECUTE_AT, flags);
511489
flags = collectFlags(before, after, Command::executesAtLeast, true, EXECUTES_AT_LEAST, flags);
@@ -531,6 +509,10 @@ public static int getFlags(Command before, Command after)
531509
flags = setChanged(PARTICIPANTS, flags);
532510
flags = setChanged(SAVE_STATUS, flags);
533511
}
512+
513+
if (after.saveStatus() != null)
514+
flags |= saveStatusMasks[after.saveStatus().ordinal()];
515+
534516
return flags;
535517
}
536518

accord-core/src/test/java/accord/impl/basic/InMemoryJournal.java

Lines changed: 13 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
import static accord.impl.CommandChange.nextSetField;
8585
import static accord.impl.CommandChange.setChanged;
8686
import static accord.impl.CommandChange.setFieldIsNull;
87+
import static accord.impl.CommandChange.setFieldIsNullAndChanged;
8788
import static accord.impl.CommandChange.toIterableSetFields;
8889
import static accord.impl.CommandChange.unsetFieldIsNull;
8990
import static accord.impl.CommandChange.unsetIterable;
@@ -172,10 +173,10 @@ private Builder reconstruct(List<Diff> saved, Load load)
172173
if (saved == null)
173174
return null;
174175

175-
// TODO (expected): match C* and visit in reverse order
176176
Builder builder = null;
177-
for (Diff diff : saved)
177+
for (int i = saved.size() - 1; i >= 0; i--)
178178
{
179+
Diff diff = saved.get(i);
179180
if (builder == null)
180181
builder = new Builder(diff.txnId, load);
181182
builder.apply(diff);
@@ -560,70 +561,28 @@ private void apply(Diff diff)
560561
{
561562
Field field = nextSetField(iterable);
562563

563-
this.flags = setChanged(field, this.flags);
564+
// Since we are iterating in reverse order, we skip the fields that were
565+
// set by entries writer later (i.e. already read ones).
566+
if (isChanged(field, this.flags) || isNull(field, mask))
567+
{
568+
iterable = unsetIterable(field, iterable);
569+
continue;
570+
}
571+
564572
if (isNull(field, diff.flags))
565573
{
566-
this.flags = setFieldIsNull(field, this.flags);
567-
setNull(field);
574+
this.flags = setFieldIsNullAndChanged(field, this.flags);
568575
}
569576
else
570577
{
571-
this.flags = unsetFieldIsNull(field, this.flags);
578+
this.flags = setChanged(field, this.flags);
572579
deserialize(diff, field);
573580
}
574581

575582
iterable = unsetIterable(field, iterable);
576583
}
577584
}
578585

579-
private void setNull(Field field)
580-
{
581-
switch (field)
582-
{
583-
case EXECUTE_AT:
584-
executeAt = null;
585-
break;
586-
case EXECUTES_AT_LEAST:
587-
executeAtLeast = null;
588-
break;
589-
case MIN_UNIQUE_HLC:
590-
minUniqueHlc = 0;
591-
break;
592-
case SAVE_STATUS:
593-
saveStatus = null;
594-
break;
595-
case DURABILITY:
596-
durability = null;
597-
break;
598-
case ACCEPTED:
599-
acceptedOrCommitted = null;
600-
break;
601-
case PROMISED:
602-
promised = null;
603-
break;
604-
case PARTICIPANTS:
605-
participants = null;
606-
break;
607-
case PARTIAL_TXN:
608-
partialTxn = null;
609-
break;
610-
case PARTIAL_DEPS:
611-
partialDeps = null;
612-
break;
613-
case WAITING_ON:
614-
waitingOn = null;
615-
break;
616-
case WRITES:
617-
writes = null;
618-
break;
619-
case RESULT:
620-
result = null;
621-
break;
622-
case CLEANUP:
623-
throw new IllegalStateException();
624-
}
625-
}
626-
627586
private void deserialize(Diff diff, Field field)
628587
{
629588
switch (field)

0 commit comments

Comments
 (0)