This is an automated email from the ASF dual-hosted git repository.

dcapwell pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra-accord.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 5626c7c  CASSANDRA-17112: Publish StrictSerializabilityVerifier so 
Cassandra may use, and fixed bugs found with the integration (#18)
5626c7c is described below

commit 5626c7c11400d4cf6d01a8e22517b53a83f5c512
Author: dcapwell <[email protected]>
AuthorDate: Mon Jan 23 11:45:13 2023 -0800

    CASSANDRA-17112: Publish StrictSerializabilityVerifier so Cassandra may 
use, and fixed bugs found with the integration (#18)
---
 accord-core/build.gradle                           |   9 +
 .../main/java/accord/impl/SimpleProgressLog.java   |   9 +-
 .../test/java/accord/verify/HistoryViolation.java  |   4 +
 .../verify/StrictSerializabilityVerifier.java      | 153 ++++++++++-----
 .../verify/StrictSerializabilityVerifierTest.java  | 216 ++++++++++++++++++++-
 5 files changed, 330 insertions(+), 61 deletions(-)

diff --git a/accord-core/build.gradle b/accord-core/build.gradle
index 7db65e9..146f9fa 100644
--- a/accord-core/build.gradle
+++ b/accord-core/build.gradle
@@ -48,6 +48,10 @@ task burn(type: JavaExec) {
 
 // the team is used to 'install' so make sure that still publishes locally
 task install(dependsOn: publishToMavenLocal)
+task testJar(type: Jar, dependsOn: testClasses) {
+  archiveClassifier = 'tests'
+  from sourceSets.test.output
+}
 
 publishing {
   publications {
@@ -58,6 +62,11 @@ publishing {
       }
       from components.java
     }
+
+    test(MavenPublication) {
+      artifactId = 'accord'
+      artifact testJar
+    }
   }
 }
 
diff --git a/accord-core/src/main/java/accord/impl/SimpleProgressLog.java 
b/accord-core/src/main/java/accord/impl/SimpleProgressLog.java
index 7fff500..e27553f 100644
--- a/accord-core/src/main/java/accord/impl/SimpleProgressLog.java
+++ b/accord-core/src/main/java/accord/impl/SimpleProgressLog.java
@@ -126,11 +126,12 @@ public class SimpleProgressLog implements 
ProgressLog.Factory
                 {
                     switch (progress)
                     {
-                        default: throw new AssertionError();
+                        default: throw new AssertionError("Unexpected 
progress: " + progress);
                         case NoneExpected:
                         case Done:
+                            return false;
                         case Investigating:
-                            throw new IllegalStateException();
+                            throw new IllegalStateException("Unexpected 
progress: " + progress);
                         case Expected:
                             Invariants.paranoid(!isFree());
                             progress = NoProgress;
@@ -209,11 +210,11 @@ public class SimpleProgressLog implements 
ProgressLog.Factory
                     setProgress(Investigating);
                     switch (status)
                     {
-                        default: throw new AssertionError();
+                        default: throw new AssertionError("Unexpected status: 
" + status);
                         case NotWitnessed: // can't make progress if we 
haven't witnessed it yet
                         case Committed: // can't make progress if we aren't 
yet ReadyToExecute
                         case Done: // shouldn't be trying to make progress, as 
we're done
-                            throw new IllegalStateException();
+                            throw new IllegalStateException("Unexpected 
status: " + status);
 
                         case Uncommitted:
                         case ReadyToExecute:
diff --git a/accord-core/src/test/java/accord/verify/HistoryViolation.java 
b/accord-core/src/test/java/accord/verify/HistoryViolation.java
index 5df0573..6e31ceb 100644
--- a/accord-core/src/test/java/accord/verify/HistoryViolation.java
+++ b/accord-core/src/test/java/accord/verify/HistoryViolation.java
@@ -27,4 +27,8 @@ public class HistoryViolation extends AssertionError
         super(detailMessage);
         this.primaryKey = primaryKey;
     }
+
+    public int primaryKey() {
+        return primaryKey;
+    }
 }
diff --git 
a/accord-core/src/test/java/accord/verify/StrictSerializabilityVerifier.java 
b/accord-core/src/test/java/accord/verify/StrictSerializabilityVerifier.java
index 44374c1..98e6217 100644
--- a/accord-core/src/test/java/accord/verify/StrictSerializabilityVerifier.java
+++ b/accord-core/src/test/java/accord/verify/StrictSerializabilityVerifier.java
@@ -30,7 +30,9 @@ import java.util.TreeSet;
 import java.util.function.Consumer;
 import java.util.stream.IntStream;
 
-import accord.utils.Invariants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import static java.util.stream.Collectors.joining;
 
 /**
  * Nomenclature:
@@ -43,34 +45,36 @@ import accord.utils.Invariants;
  *               two kinds: 1) those values for keys [B,C..) at step index i 
for a read of key A, precede key A's step index i +1
  *                          2) those values for keys [B,C..) at step index i 
for a write of key A, precede key A's step index i
  *  max predecessor: the maximum predecessor that may be reached via any 
predecessor relation
- *
+ * <p>
  * Ensure there are no timestamp cycles in the implied list of predecessors, 
i.e. that we have a strict serializable order.
  * That is, we maintain links to the maximum predecessor step for each key, at 
each step for each key, and see if we can
  * find a path of predecessors that would witness us.
- *
+ * <p>
  * TODO (low priority): find and report a path when we encounter a violation
  */
 public class StrictSerializabilityVerifier
 {
+    private static final Logger logger = 
LoggerFactory.getLogger(StrictSerializabilityVerifier.class);
+
     /**
      * A link to the maximum predecessor node for a given key reachable from 
the transitive closure of predecessor
      * relations from a given register's observation (i.e. for a given 
sequence observed for a given key).
-     *
+     * <p>
      * A predecessor is an absolute happens-before relationship. This is 
created either:
      *  1) by witnessing some read for key A coincident with a write for key B,
      *     therefore the write for key B happened strictly after the write for 
key A; or
      *  2) any value for key A witnessed alongside a step index i for key B 
happens before i+1 for key B.
-     *
+     * <p>
      * For every observation step index i for key A, we have an outgoing 
MaxPredecessor link to every key.
      * This object both maintains the current maximum predecessor step index 
reachable via the transitive closure
      * of happens-before relationships, but represents a back-link from that 
step index for the referred-to key,
      * so that when its own predecessor memoized maximum predecessor values 
are updated we can propagate them here.
-     *
+     * <p>
      * In essence, each node in the happens-before graph maintains a link to 
every possible frontier of its transitive
      * closure in the graph, so that each time that frontier is updated the 
internal nodes that reference it are updated
      * to the new frontier. This ensures ~computationally optimal maintenance 
of this transitive closure at the expense
      * of quadratic memory utilisation, but for small numbers of unique keys 
this remains quite manageable (i.e. steps*keys^2)
-     *
+     * <p>
      * This graph can be interpreted quite simply: if any step index for a key 
can reach itself (or a successor) via
      * the transitive closure of happens-before relations then there is a 
serializability violation.
      */
@@ -143,6 +147,7 @@ public class StrictSerializabilityVerifier
     static class UnknownStepPredecessor extends MaxPredecessor
     {
         final UnknownStepHolder holder;
+
         UnknownStepPredecessor(Step successor, UnknownStepHolder holder)
         {
             super(successor.ofKey, successor, holder.step.ofKey);
@@ -228,6 +233,7 @@ public class StrictSerializabilityVerifier
         Map<Step, UnknownStepPredecessor> unknownStepPredecessors;
         Runnable onChange;
 
+        final int writeValue;
         int ofStepIndex;
 
         /**
@@ -247,9 +253,10 @@ public class StrictSerializabilityVerifier
 
         int maxPredecessorWrittenAfter = Integer.MIN_VALUE;
 
-        Step(int key, int stepIndex, int keyCount)
+        Step(int key, int stepIndex, int keyCount, int writeValue)
         {
             super(key, null, key);
+            this.writeValue = writeValue;
             this.ofStep = this;
             this.ofStepIndex = stepIndex;
             this.maxPeers = new int[keyCount];
@@ -312,21 +319,21 @@ public class StrictSerializabilityVerifier
             successor.predecessorStep = this;
         }
 
-        boolean updatePeers(int[] newPeers, UnknownStepHolder[] unknownSteps)
+        boolean updatePeers(int[] newPeerSteps, UnknownStepHolder[] 
newBlindWrites)
         {
             boolean updated = false;
-            for (int key = 0 ; key < newPeers.length ; ++key)
+            for (int key = 0 ; key < newPeerSteps.length ; ++key)
             {
-                int newPeer = newPeers[key];
+                int newPeer = newPeerSteps[key];
                 int maxPeer = maxPeers[key];
                 if (newPeer > maxPeer)
                 {
                     updated = true;
-                    maxPeers[key] = newPeers[key];
+                    maxPeers[key] = newPeerSteps[key];
                 }
-                if (unknownSteps != null)
+                if (newBlindWrites != null)
                 {
-                    UnknownStepHolder unknownStep = unknownSteps[key];
+                    UnknownStepHolder unknownStep = newBlindWrites[key];
                     if (unknownStep != null && unknownStep.step != this)
                     {
                         if (unknownStepPeers == null)
@@ -443,23 +450,29 @@ public class StrictSerializabilityVerifier
         @Override
         public String toString()
         {
-            return "{key: " + ofKey +
-                   ", peers:" + Arrays.toString(maxPeers) +
-                   ", preds:" + Arrays.toString(maxPredecessors) +
-                   (unknownStepPeers != null ? ", peers?:" + unknownStepPeers 
: "") +
-                   (unknownStepPredecessors != null ? ", preds?:" + 
unknownStepPredecessors.values() : "") +
-                   "}";
+            StringBuilder builder = new StringBuilder("{key: " + ofKey);
+            if (ofStepIndex < 0)
+                builder.append(", value: ").append(writeValue);
+            builder.append(", peers:").append(Arrays.toString(maxPeers))
+                    .append(", 
preds:").append(Arrays.toString(maxPredecessors))
+                    .append(unknownStepPeers != null ? ", peers?:" + 
unknownStepPeers : "")
+                    .append(unknownStepPredecessors != null ? ", preds?:" + 
unknownStepPredecessors.values() : "")
+                    .append("}");
+            return builder.toString();
         }
     }
 
+    // TODO (expected, testing): rethink this concept - should it be a Step? 
Does it provide any value? If not, it's more confusing.
     class FutureWrites extends Step
     {
-
         /**
          * Any write value we don't know the step index for because we did not 
perform a coincident read;
          * we wait until we witness a read containing the
-         *
-         * TODO (desired, testing): handle writes with unknown outcome
+         * <p>
+         * TODO: report a violation if we have witnessed a sequence missing 
any of these deferred operations
+         *       that started after they finished
+         * <p>
+         * TODO: handle writes with unknown outcome
          */
 
         final Map<Integer, UnknownStepHolder> byWriteValue = new HashMap<>();
@@ -467,7 +480,7 @@ public class StrictSerializabilityVerifier
 
         FutureWrites(int key, int keyCount)
         {
-            super(key, Integer.MAX_VALUE, keyCount);
+            super(key, Integer.MAX_VALUE, keyCount, Integer.MIN_VALUE);
         }
 
         void newSequence(int[] oldSequence, int[] newSequence)
@@ -489,10 +502,16 @@ public class StrictSerializabilityVerifier
                 recompute();
         }
 
+        @Override
+        boolean witnessedBetween(int start, int end, boolean isWrite)
+        {
+            return false;
+        }
+
         @Override
         boolean updatePeers(int[] newPeers, UnknownStepHolder[] unknownSteps)
         {
-            return unknownSteps[ofKey].step.updatePeers(newPeers, 
unknownSteps);
+            return false;
         }
 
         @Override
@@ -506,9 +525,14 @@ public class StrictSerializabilityVerifier
         @Override
         boolean updatePredecessorsOfWrite(int[][] reads, int[] writes, 
StrictSerializabilityVerifier verifier)
         {
-            for (UnknownStepHolder holder : byWriteValue.values())
-                holder.step.updatePredecessorsOfWrite(reads, writes, verifier);
-            return super.updatePredecessorsOfWrite(reads, writes, verifier);
+            // TODO (required): this evidently wasn't carefully considered at 
the time, and removing it improves things
+            //                  but what if anything this trying to achieve?
+            //                  probably we DO want to update the predecessors 
of any NEW step. perhaps we also want
+            //                  and we probably want some additional unit 
tests around blind writes
+//            for (UnknownStepHolder holder : byWriteValue.values())
+//                holder.step.updatePredecessorsOfWrite(reads, writes, 
verifier);
+//            return super.updatePredecessorsOfWrite(reads, writes, verifier);
+            return false;
         }
 
         void recompute()
@@ -520,15 +544,15 @@ public class StrictSerializabilityVerifier
                 witnessedBetween(deferred.start, deferred.end, true);
         }
 
-        void register(UnknownStepHolder[] unknownSteps, int start, int end)
+        void register(UnknownStepHolder[] newBlindWrites, int start, int end)
         {
-            UnknownStepHolder unknownStep = unknownSteps[ofKey];
+            UnknownStepHolder unknownStep = newBlindWrites[ofKey];
             if (null != byWriteValue.putIfAbsent(unknownStep.writeValue, 
unknownStep))
                 throw new AssertionError();
             if (null != byTimestamp.putIfAbsent(end, unknownStep))
                 throw new AssertionError();
             // TODO (desired, testing): verify this propagation by unit test
-            unknownSteps[ofKey].step.receiveKnowledgePhasedPredecessors(this, 
StrictSerializabilityVerifier.this);
+            
newBlindWrites[ofKey].step.receiveKnowledgePhasedPredecessors(this, 
StrictSerializabilityVerifier.this);
         }
 
         public void checkForUnwitnessed(int start)
@@ -539,6 +563,11 @@ public class StrictSerializabilityVerifier
                 throw new HistoryViolation(ofKey, "Writes not witnessed: " + 
notWitnessed);
             }
         }
+
+        public String toString()
+        {
+            return "FutureWrites:[" + byTimestamp.values().stream().map(s -> 
s.writeValue).map(Object::toString).collect(joining(",")) + ']';
+        }
     }
 
     /**
@@ -559,6 +588,12 @@ public class StrictSerializabilityVerifier
             this.futureWrites = new FutureWrites(key, keyCount);
         }
 
+        void print()
+        {
+            for (Map.Entry<Integer, UnknownStepHolder> e : 
futureWrites.byWriteValue.entrySet())
+                logger.error("{}: {} -> ({}, {}, {})", key, e.getKey(), 
e.getValue().writeValue, e.getValue().start, e.getValue().end);
+        }
+
         private void updateSequence(int[] sequence, int maybeWrite)
         {
             for (int i = 0, max = Math.min(sequence.length, 
this.sequence.length) ; i < max ; ++i)
@@ -592,7 +627,7 @@ public class StrictSerializabilityVerifier
                 steps = Arrays.copyOf(steps, Math.max(step + 1, steps.length * 
2));
 
             if (steps[step] == null)
-                insert(new Step(key, step, keyCount));
+                insert(new Step(key, step, keyCount, step == 0 ? -1 : 
sequence[step - 1]));
             return steps[step];
         }
 
@@ -618,28 +653,27 @@ public class StrictSerializabilityVerifier
                 step.setSuccessor(successor);
                 propagateToDirectSuccessor(step, successor);
             }
-
         }
 
-        private void updatePeersAndPredecessors(int[] newPeers, int[][] reads, 
int[] writes, int start, int end, UnknownStepHolder[] unknownSteps)
+        private void updatePeersAndPredecessors(int[] newPeerSteps, int[][] 
reads, int[] writes, int start, int end, UnknownStepHolder[] newBlindWrites)
         {
             Step step;
-            boolean updated = false;
-            if (newPeers[key] >= 0)
+            boolean updated;
+            if (newPeerSteps[key] >= 0)
             {
-                step = step(newPeers[key]);
+                step = step(newPeerSteps[key]);
             }
-            else if (unknownSteps != null && unknownSteps[key] != null)
+            else if (newBlindWrites != null && newBlindWrites[key] != null)
             {
                 assert writes[key] >= 0;
-                futureWrites.register(unknownSteps, start, end);
-                step = futureWrites;
+                futureWrites.register(newBlindWrites, start, end);
+                step = newBlindWrites[key].step;
             }
             else
             {
                 throw new IllegalStateException();
             }
-            updated = step.updatePeers(newPeers, unknownSteps);
+            updated = step.updatePeers(newPeerSteps, newBlindWrites);
             updated |= step.updatePredecessorsOfWrite(reads, writes, 
StrictSerializabilityVerifier.this);
             updated |= step.witnessedBetween(start, end, writes[key] >= 0);
             if (updated)
@@ -680,7 +714,13 @@ public class StrictSerializabilityVerifier
             if (step.successor != null)
                 propagateToDirectSuccessor(step, step.successor);
             if (step.onChange != null)
-                step.onChange.run();
+            {
+                // prevent reentrancy
+                Runnable run = step.onChange;
+                step.onChange = null;
+                run.run();
+                step.onChange = run;
+            }
         }
 
         void checkForUnwitnessed(int start)
@@ -735,7 +775,7 @@ public class StrictSerializabilityVerifier
 
     /**
      * Buffer a new read observation.
-     *
+     * <p>
      * Note that this should EXCLUDE any witnessed write for this key.
      * This is to simplify the creation of direct happens-before edges with 
observations for other keys
      * that are implied by the witnessing of a write (and is also marginally 
more efficient).
@@ -774,21 +814,24 @@ public class StrictSerializabilityVerifier
             {
                 int i = Arrays.binarySearch(registers[k].sequence, 
bufWrites[k]);
                 if (i >= 0) bufNewPeerSteps[k] = i + 1;
-                else bufUnknownSteps[k] = new UnknownStepHolder(bufWrites[k], 
start, end, new Step(k, Integer.MAX_VALUE, keyCount));
+                else bufUnknownSteps[k] = new UnknownStepHolder(bufWrites[k], 
start, end, new Step(k, Integer.MAX_VALUE, keyCount, bufWrites[k]));
                 hasUnknownSteps |= i < 0;
             }
         }
 
+        for (int k = 0; k < bufReads.length ; ++k)
+        {
+            if (bufReads[k] != null)
+                registers[k].updateSequence(bufReads[k], bufWrites[k]);
+        }
         for (int k = 0; k < bufReads.length ; ++k)
         {
             if (bufWrites[k] >= 0 || bufReads[k] != null)
             {
-                if (bufReads[k] != null)
-                    registers[k].updateSequence(bufReads[k], bufWrites[k]);
-
                 registers[k].updatePeersAndPredecessors(bufNewPeerSteps, 
bufReads, bufWrites, start, end, hasUnknownSteps ? bufUnknownSteps : null);
+                if (bufReads[k] != null)
+                    registers[k].checkForUnwitnessed(start);
             }
-            registers[k].checkForUnwitnessed(start);
         }
 
         refreshTransitive();
@@ -801,4 +844,18 @@ public class StrictSerializabilityVerifier
             registers[next.ofKey].propagateToSuccessor(next.predecessorStep, 
next.ofStep);
         }
     }
+
+    public void print()
+    {
+        for (Register r : registers)
+        {
+            if (r == null) continue;
+            r.print();
+        }
+    }
+
+    public void print(int k)
+    {
+        registers[k].print();
+    }
 }
diff --git 
a/accord-core/src/test/java/accord/verify/StrictSerializabilityVerifierTest.java
 
b/accord-core/src/test/java/accord/verify/StrictSerializabilityVerifierTest.java
index 78eadf4..c78513f 100644
--- 
a/accord-core/src/test/java/accord/verify/StrictSerializabilityVerifierTest.java
+++ 
b/accord-core/src/test/java/accord/verify/StrictSerializabilityVerifierTest.java
@@ -19,9 +19,15 @@
 package accord.verify;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.function.Consumer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
 
+import com.carrotsearch.hppc.IntHashSet;
+import com.carrotsearch.hppc.IntSet;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -43,14 +49,35 @@ public class StrictSerializabilityVerifierTest
 
         History w(int writeKey, int writeSeq)
         {
-            return rw(writeKey, writeSeq, null);
+            return rw(writeKey, writeSeq, (int[])null);
+        }
+
+        History ws(int ... writes)
+        {
+            return rw(writes, (int[])null);
         }
 
         History rw(int writeKey, int writeSeq, int ... reads)
+        {
+            int[] writes;
+            if (writeKey < 0)
+            {
+                writes = new int[0];
+            }
+            else
+            {
+                writes = new int[writeKey + 1];
+                Arrays.fill(writes, -1);
+                writes[writeKey] = writeSeq;
+            }
+            return rw(writes, reads);
+        }
+
+        History rw(int[] writes, int ... reads)
         {
             int start = overlap > 0 ? this.overlap : ++nextAt;
             int end = ++nextAt;
-            observations.add(new Observation(writeKey, writeSeq, reads, start, 
end));
+            observations.add(new Observation(writes, reads, start, end));
             return this;
         }
 
@@ -96,8 +123,11 @@ public class StrictSerializabilityVerifierTest
                             verifier.witnessRead(key, 
SEQUENCES[observation.reads[key] + 1]);
                     }
                 }
-                if (observation.writeKey >= 0)
-                    verifier.witnessWrite(observation.writeKey, 
observation.writeSeq);
+                for (int j = 0 ; j < observation.writes.length ; ++j)
+                {
+                    if (observation.writes[j] >= 0)
+                        verifier.witnessWrite(j, observation.writes[j]);
+                }
 
                 if (i == observations.size() - 1) onLast.accept(() -> 
verifier.apply(observation.start, observation.end));
                 else verifier.apply(observation.start, observation.end);
@@ -107,15 +137,13 @@ public class StrictSerializabilityVerifierTest
 
     static class Observation
     {
-        final int writeKey;
-        final int writeSeq;
+        final int[] writes;
         final int[] reads;
         final int start, end;
 
-        Observation(int writeKey, int writeSeq, int[] reads, int start, int 
end)
+        Observation(int[] writes, int[] reads, int start, int end)
         {
-            this.writeKey = writeKey;
-            this.writeSeq = writeSeq;
+            this.writes = writes;
             this.reads = reads;
             this.start = start;
             this.end = end;
@@ -167,4 +195,174 @@ public class StrictSerializabilityVerifierTest
         new History(1).r(0).w(0, 1).r(1).assertNoViolation();
         new History(1).rw(0, 1, 0).r(1).assertNoViolation();
     }
+
+    @Test
+    public void blindWrites()
+    {
+        new History(2).ws(1, 1).r(1, -1).ws(-1, 2).assertNoViolation();
+        new History(2).ws(1, 1).ws(-1, 2).assertNoViolation();
+        new History(2).ws(1, 1).ws(2, 2).r(2).assertNoViolation();
+        new History(2).ws(1, 1).ws(2, 2).r(2).r(1).assertViolation();
+        new History(2).ws(1, 1).overlap().ws(2, 
2).r(2).r(1).deoverlap().r(2).assertNoViolation();
+        new History(2).ws(1, 1).ws(2, 2).r(1).assertViolation();
+    }
+
+    private void fromLog(String log)
+    {
+        IntSet pks = new IntHashSet();
+        class Read
+        {
+            final int pk, id, count;
+            final int[] seq;
+
+            Read(int pk, int id, int count, int[] seq)
+            {
+                this.pk = pk;
+                this.id = id;
+                this.count = count;
+                this.seq = seq;
+            }
+        }
+        class Write
+        {
+            final int pk, id;
+            final boolean success;
+
+            Write(int pk, int id, boolean success)
+            {
+                this.pk = pk;
+                this.id = id;
+                this.success = success;
+            }
+        }
+        class Witness
+        {
+            final int start, end;
+            final List<Object> actions = new ArrayList<>();
+
+            Witness(int start, int end)
+            {
+                this.start = start;
+                this.end = end;
+            }
+
+            public void read(int pk, int id, int count, int[] seq)
+            {
+                actions.add(new Read(pk, id, count, seq));
+            }
+
+            public void write(int pk, int id, boolean success)
+            {
+                actions.add(new Write(pk, id, success));
+            }
+
+            public void process(StrictSerializabilityVerifier verifier)
+            {
+                verifier.begin();
+                for (Object a : actions)
+                {
+                    if (a instanceof Read)
+                    {
+                        Read read = (Read) a;
+                        verifier.witnessRead(read.pk, read.seq);
+                    }
+                    else
+                    {
+                        Write write = (Write) a;
+                        if (write.success)
+                            verifier.witnessWrite(write.pk, write.id);
+                    }
+                }
+                verifier.apply(start, end);
+            }
+        }
+        List<Witness> witnesses = new ArrayList<>();
+        Witness current = null;
+        for (String line : log.split("\n"))
+        {
+            if (line.startsWith("Witness"))
+            {
+                if (current != null)
+                {
+                    witnesses.add(current);
+                    current = null;
+                }
+                Matcher matcher = Pattern.compile("Witness\\(start=(.+), 
end=(.+)\\)").matcher(line);
+                if (!matcher.find()) throw new AssertionError("Unable to match 
start/end of " + line);
+                current = new Witness(Integer.parseInt(matcher.group(1)), 
Integer.parseInt(matcher.group(2)));
+            }
+            else if (line.startsWith("\tread"))
+            {
+                Matcher matcher = Pattern.compile("\tread\\(pk=(.+), id=(.+), 
count=(.+), seq=\\[(.*)\\]\\)").matcher(line);
+                if (!matcher.find()) throw new AssertionError("Unable to match 
read of " + line);
+                int pk = Integer.parseInt(matcher.group(1));
+                pks.add(pk);
+                int id = Integer.parseInt(matcher.group(2));
+                int count = Integer.parseInt(matcher.group(3));
+                String seqStr = matcher.group(4);
+                int[] seq = seqStr.isEmpty() ? new int[0] : 
Stream.of(seqStr.split(",")).map(String::trim).mapToInt(Integer::parseInt).toArray();
+                current.read(pk, id, count, seq);
+            }
+            else if (line.startsWith("\twrite"))
+            {
+                Matcher matcher = Pattern.compile("\twrite\\(pk=(.+), id=(.+), 
success=(.+)\\)").matcher(line);
+                if (!matcher.find()) throw new AssertionError("Unable to match 
write of " + line);
+                int pk = Integer.parseInt(matcher.group(1));
+                pks.add(pk);
+                int id = Integer.parseInt(matcher.group(2));
+                boolean success = Boolean.parseBoolean(matcher.group(3));
+                current.write(pk, id, success);
+            }
+            else
+            {
+                throw new IllegalArgumentException("Unknow line: " + line);
+            }
+        }
+        if (current != null)
+        {
+            witnesses.add(current);
+            current = null;
+        }
+        int[] keys = pks.toArray();
+        Arrays.sort(keys);
+        StrictSerializabilityVerifier validator = new 
StrictSerializabilityVerifier(3);
+        for (Witness w : witnesses)
+        {
+            w.process(validator);
+        }
+    }
+
+    @Test
+    public void seenBehavior()
+    {
+        fromLog("Witness(start=4, end=7)\n" +
+                "\tread(pk=0, id=2, count=0, seq=[])\n" +
+                "\twrite(pk=0, id=2, success=true)\n" +
+                "Witness(start=3, end=8)\n" +
+                "\tread(pk=2, id=0, count=0, seq=[])\n" +
+                "\twrite(pk=2, id=0, success=true)\n" +
+                "\twrite(pk=1, id=0, success=true)\n" +
+                "Witness(start=5, end=9)\n" +
+                "\tread(pk=0, id=3, count=1, seq=[2])\n" +
+                "\twrite(pk=0, id=3, success=true)\n" +
+                "Witness(start=2, end=10)\n" +
+                "\twrite(pk=2, id=1, success=true)\n" +
+                "\twrite(pk=1, id=1, success=true)\n" +
+                "Witness(start=6, end=11)\n" +
+                "\tread(pk=0, id=4, count=2, seq=[2, 3])\n" +
+                "\twrite(pk=0, id=4, success=true)\n" +
+                "Witness(start=12, end=14)\n" +
+                "\twrite(pk=0, id=5, success=true)\n" +
+                "Witness(start=13, end=16)\n" +
+                "\tread(pk=1, id=6, count=2, seq=[0, 1])\n" +
+                "\twrite(pk=1, id=6, success=true)\n" +
+                "\twrite(pk=2, id=6, success=true)\n" +
+                "Witness(start=15, end=18)\n" +
+                "\tread(pk=0, id=7, count=4, seq=[2, 3, 4, 5])\n" +
+                "\twrite(pk=0, id=7, success=true)\n" +
+                "Witness(start=17, end=20)\n" +
+                "\tread(pk=1, id=8, count=3, seq=[0, 1, 6])\n" +
+                "\twrite(pk=1, id=8, success=true)\n" +
+                "\twrite(pk=2, id=8, success=true)\n");
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to