Skip to content

Commit a2ac02b

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

File tree

2 files changed

+55
-114
lines changed

2 files changed

+55
-114
lines changed

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

Lines changed: 42 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import java.util.function.BiPredicate;
2222
import java.util.function.Function;
23+
import java.util.function.Predicate;
2324
import java.util.function.ToLongFunction;
2425

2526
import com.google.common.annotations.VisibleForTesting;
@@ -52,13 +53,13 @@
5253
import static accord.impl.CommandChange.Field.EXECUTES_AT_LEAST;
5354
import static accord.impl.CommandChange.Field.EXECUTE_AT;
5455
import static accord.impl.CommandChange.Field.FIELDS;
56+
import static accord.impl.CommandChange.Field.MIN_UNIQUE_HLC;
5557
import static accord.impl.CommandChange.Field.PARTIAL_DEPS;
5658
import static accord.impl.CommandChange.Field.PARTIAL_TXN;
5759
import static accord.impl.CommandChange.Field.PARTICIPANTS;
5860
import static accord.impl.CommandChange.Field.PROMISED;
5961
import static accord.impl.CommandChange.Field.RESULT;
6062
import static accord.impl.CommandChange.Field.SAVE_STATUS;
61-
import static accord.impl.CommandChange.Field.MIN_UNIQUE_HLC;
6263
import static accord.impl.CommandChange.Field.WAITING_ON;
6364
import static accord.impl.CommandChange.Field.WRITES;
6465
import static accord.local.Cleanup.NO;
@@ -74,7 +75,11 @@
7475
import static accord.local.Command.Truncated.invalidated;
7576
import static accord.local.Command.Truncated.vestigial;
7677
import static accord.local.StoreParticipants.Filter.LOAD;
78+
import static accord.primitives.Known.Definition.DefinitionErased;
79+
import static accord.primitives.Known.KnownDeps.DepsErased;
7780
import static accord.primitives.Known.KnownExecuteAt.ApplyAtKnown;
81+
import static accord.primitives.Known.KnownExecuteAt.ExecuteAtErased;
82+
import static accord.primitives.Known.Outcome.WasApply;
7883
import static accord.primitives.Status.Durability.NotDurable;
7984

8085
public class CommandChange
@@ -101,6 +106,37 @@ public enum Field
101106
public static final Field[] FIELDS = values();
102107
}
103108

109+
/**
110+
* SaveStatus.Known contains information about erased / nullified fields,
111+
* which we can use in order to mark the corresponding fields as changed
112+
* and setting them to null when they are erased.
113+
*/
114+
public static int[] saveStatusMasks;
115+
116+
static
117+
{
118+
saveStatusMasks = new int[SaveStatus.values().length];
119+
for (int i = 0; i < saveStatusMasks.length; i++)
120+
{
121+
SaveStatus saveStatus = SaveStatus.forOrdinal(i);
122+
int mask = 0;
123+
if (forceFieldChangedToNullFlag(saveStatus, saveStatus.known::is, DepsErased))
124+
mask |= setFieldIsNullAndChanged(PARTIAL_DEPS, mask);
125+
if (forceFieldChangedToNullFlag(saveStatus, saveStatus.known::is, ExecuteAtErased))
126+
mask |= setFieldIsNullAndChanged(EXECUTE_AT, mask);
127+
if (forceFieldChangedToNullFlag(saveStatus, saveStatus.known::is, DefinitionErased))
128+
mask |= setFieldIsNullAndChanged(PARTIAL_TXN, mask);
129+
if (forceFieldChangedToNullFlag(saveStatus, saveStatus.known::is, WasApply))
130+
mask |= setFieldIsNullAndChanged(RESULT, mask);
131+
saveStatusMasks[i] = mask;
132+
}
133+
}
134+
135+
private static <T> boolean forceFieldChangedToNullFlag(SaveStatus saveStatus, Predicate<T> predicate, T erased)
136+
{
137+
return saveStatus == SaveStatus.Vestigial || predicate.test(erased);
138+
}
139+
104140
public static class Builder
105141
{
106142
protected final int mask;
@@ -151,72 +187,16 @@ public Builder()
151187
this(ALL);
152188
}
153189

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-
170190
public SaveStatus saveStatus()
171191
{
172192
return saveStatus;
173193
}
174194

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-
190195
public StoreParticipants participants()
191196
{
192197
return participants;
193198
}
194199

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-
220200
public void clear()
221201
{
222202
flags = 0;
@@ -286,7 +266,7 @@ public Cleanup shouldCleanup(Input input, Agent agent, RedundantBefore redundant
286266

287267
public Builder maybeCleanup(Cleanup cleanup)
288268
{
289-
if (saveStatus() == null)
269+
if (saveStatus == null)
290270
return this;
291271

292272
switch (cleanup)
@@ -531,6 +511,10 @@ public static int getFlags(Command before, Command after)
531511
flags = setChanged(PARTICIPANTS, flags);
532512
flags = setChanged(SAVE_STATUS, flags);
533513
}
514+
515+
if (after.saveStatus() != null)
516+
flags |= saveStatusMasks[after.saveStatus().ordinal()];
517+
534518
return flags;
535519
}
536520

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

Lines changed: 13 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,8 @@
8383
import static accord.impl.CommandChange.isNull;
8484
import static accord.impl.CommandChange.nextSetField;
8585
import static accord.impl.CommandChange.setChanged;
86-
import static accord.impl.CommandChange.setFieldIsNull;
86+
import static accord.impl.CommandChange.setFieldIsNullAndChanged;
8787
import static accord.impl.CommandChange.toIterableSetFields;
88-
import static accord.impl.CommandChange.unsetFieldIsNull;
8988
import static accord.impl.CommandChange.unsetIterable;
9089
import static accord.impl.CommandChange.validateFlags;
9190
import static accord.local.Cleanup.Input.FULL;
@@ -172,10 +171,10 @@ private Builder reconstruct(List<Diff> saved, Load load)
172171
if (saved == null)
173172
return null;
174173

175-
// TODO (expected): match C* and visit in reverse order
176174
Builder builder = null;
177-
for (Diff diff : saved)
175+
for (int i = saved.size() - 1; i >= 0; i--)
178176
{
177+
Diff diff = saved.get(i);
179178
if (builder == null)
180179
builder = new Builder(diff.txnId, load);
181180
builder.apply(diff);
@@ -560,70 +559,28 @@ private void apply(Diff diff)
560559
{
561560
Field field = nextSetField(iterable);
562561

563-
this.flags = setChanged(field, this.flags);
562+
// Since we are iterating in reverse order, we skip the fields that were
563+
// set by entries writer later (i.e. already read ones).
564+
if (isChanged(field, this.flags) || isNull(field, mask))
565+
{
566+
iterable = unsetIterable(field, iterable);
567+
continue;
568+
}
569+
564570
if (isNull(field, diff.flags))
565571
{
566-
this.flags = setFieldIsNull(field, this.flags);
567-
setNull(field);
572+
this.flags = setFieldIsNullAndChanged(field, this.flags);
568573
}
569574
else
570575
{
571-
this.flags = unsetFieldIsNull(field, this.flags);
576+
this.flags = setChanged(field, this.flags);
572577
deserialize(diff, field);
573578
}
574579

575580
iterable = unsetIterable(field, iterable);
576581
}
577582
}
578583

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-
627584
private void deserialize(Diff diff, Field field)
628585
{
629586
switch (field)

0 commit comments

Comments
 (0)