Skip to content

Commit 1f1e5b1

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

File tree

2 files changed

+50
-112
lines changed

2 files changed

+50
-112
lines changed

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

Lines changed: 37 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,33 @@ 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+
boolean isVestigial = saveStatus == SaveStatus.Vestigial;
122+
int mask = 0;
123+
if (isVestigial || saveStatus.known.deps() == DepsErased)
124+
mask |= setFieldIsNullAndChanged(PARTIAL_DEPS, mask);
125+
if (isVestigial || saveStatus.known.executeAt() == ExecuteAtErased)
126+
mask |= setFieldIsNullAndChanged(EXECUTE_AT, mask);
127+
if (isVestigial || saveStatus.known.definition() == DefinitionErased)
128+
mask |= setFieldIsNullAndChanged(PARTIAL_TXN, mask);
129+
if (isVestigial || saveStatus.known.outcome() == WasApply)
130+
mask |= setFieldIsNullAndChanged(RESULT, mask);
131+
saveStatusMasks[i] = mask;
132+
}
133+
}
134+
104135
public static class Builder
105136
{
106137
protected final int mask;
@@ -151,72 +182,16 @@ public Builder()
151182
this(ALL);
152183
}
153184

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-
170185
public SaveStatus saveStatus()
171186
{
172187
return saveStatus;
173188
}
174189

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-
190190
public StoreParticipants participants()
191191
{
192192
return participants;
193193
}
194194

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-
220195
public void clear()
221196
{
222197
flags = 0;
@@ -286,7 +261,7 @@ public Cleanup shouldCleanup(Input input, Agent agent, RedundantBefore redundant
286261

287262
public Builder maybeCleanup(Cleanup cleanup)
288263
{
289-
if (saveStatus() == null)
264+
if (saveStatus == null)
290265
return this;
291266

292267
switch (cleanup)
@@ -531,6 +506,10 @@ public static int getFlags(Command before, Command after)
531506
flags = setChanged(PARTICIPANTS, flags);
532507
flags = setChanged(SAVE_STATUS, flags);
533508
}
509+
510+
if (after.saveStatus() != null)
511+
flags |= saveStatusMasks[after.saveStatus().ordinal()];
512+
534513
return flags;
535514
}
536515

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)