|
197 | 197 | ; infer the version order for that key, even if we never observe it in a
|
198 | 198 | ; read: it has to go from nil -> [x].
|
199 | 199 | (testing "single appends without reads"
|
200 |
| - (is (= {rx [ax1] ax1 []} (g rx ax1))) |
201 |
| - ; But with two appends, we can't infer any more |
202 |
| - (is (= {} (g rx ax1 ax2)))) |
| 200 | + (is (= {rx [ax1] ax1 []} (g rx ax1)))) |
| 201 | + |
| 202 | + (testing "multiple appends without reads" |
| 203 | + ; With two appends, we can't infer a precise order, but we still know ax1 |
| 204 | + ; and ax2 both had to come after rx! |
| 205 | + (is (= {rx [ax1 ax2] ax1 [] ax2 []} (g rx ax1 ax2)))) |
203 | 206 |
|
204 | 207 | (testing "duplicate inserts attempts"
|
205 | 208 | (let [ax1ry {:index 0, :type :invoke, :value [[:append :x 1] [:r :y nil]]}
|
|
414 | 417 | [t1 t1'] (pair (op "ax1ax2"))
|
415 | 418 | [t2 t2'] (pair (op "rx1"))
|
416 | 419 | [t1 t1' t2 t2' :as h] (history/index [t1 t1' t2 t2'])]
|
| 420 | + ; This is not only G1b, since it has an intermediate read, but also |
| 421 | + ; G-single, since rx1 observes ax1 but does not observe ax2! |
| 422 | + |
417 | 423 | ; G0 checker won't catch this
|
418 | 424 | (is (= {:valid? true} (c {:consistency-models nil, :anomalies [:G0]} h)))
|
419 | 425 | ; G1 will
|
|
425 | 431 | :mop [:r :x [1]]
|
426 | 432 | :element 1}]}}
|
427 | 433 | (c {:consistency-models nil, :anomalies [:G1]} h)))
|
428 |
| - ; G2 won't: even though the graph covers G1c, we don't do the specific |
429 |
| - ; G1a/b checks unless asked. |
430 |
| - (is (= {:valid? true} |
431 |
| - (c {:consistency-models nil, :anomalies [:G2]} h))) |
| 434 | + ; G2 catches G-single but won't actually report G1b: even though the |
| 435 | + ; graph covers G1c, we filter out the G1b anomaly since we weren't asked |
| 436 | + ; for it. Still report that it's not read-committed, which is... maybe |
| 437 | + ; questionable. Might change this later? |
| 438 | + (is (= {:valid? false |
| 439 | + :not #{:read-committed} |
| 440 | + :anomaly-types [:G-single]} |
| 441 | + (-> (c {:consistency-models nil, :anomalies [:G2]} h) |
| 442 | + (select-keys [:valid? :not :anomaly-types])))) |
432 | 443 | ; But, say, strict-1SR will
|
433 | 444 | (is (= {:valid? false
|
434 |
| - :anomaly-types [:G1b] |
| 445 | + :anomaly-types [:G-single :G1b] |
435 | 446 | :not #{:read-committed}
|
436 | 447 | :anomalies {:G1b [{:op t2'
|
437 | 448 | :writer t1'
|
438 | 449 | :mop [:r :x [1]]
|
439 |
| - :element 1}]}} |
| 450 | + :element 1}] |
| 451 | + :G-single |
| 452 | + [{:cycle |
| 453 | + [{:type :ok, :value [[:r :x [1]]], :index 3} |
| 454 | + {:type :ok, |
| 455 | + :value [[:append :x 1] [:append :x 2]], |
| 456 | + :index 1} |
| 457 | + {:type :ok, :value [[:r :x [1]]], :index 3}], |
| 458 | + :steps |
| 459 | + [{:type :rw, |
| 460 | + :key :x, |
| 461 | + :value 1, |
| 462 | + :value' 2, |
| 463 | + :a-mop-index 0, |
| 464 | + :b-mop-index 1} |
| 465 | + {:type :wr, |
| 466 | + :key :x, |
| 467 | + :value 1, |
| 468 | + :a-mop-index 0, |
| 469 | + :b-mop-index 0}], |
| 470 | + :type :G-single}]}} |
440 | 471 | (c {:consistency-models [:strict-serializable]} h)))))
|
441 | 472 |
|
442 | 473 | (testing "G1c"
|
|
708 | 739 | ; There's a G0 here too, but we don't care.
|
709 | 740 | (c {:consistency-models nil, :anomalies [:internal]} h))))))
|
710 | 741 |
|
| 742 | +(deftest unobserved-write-test |
| 743 | + ; When we see [:r :x [1 2]], and 1 was written by t1, 2 written by t2, and 3 |
| 744 | + ; written by t3, we can infer a dependency not only from the transaction 1 -> |
| 745 | + ; 2 but *also* from 2 -> 3: transactions which are not observed in the |
| 746 | + ; longest read must fall after the transaction which generated that value! |
| 747 | + ; |
| 748 | + ; To test this, we perform writes of 1, 2, and 3 to both x and y, but let y |
| 749 | + ; fail to observe 1. |
| 750 | + (let [[w1 w1'] (pair (op "ax1ay1")) |
| 751 | + [w2 w2'] (pair (op "ax2ay2")) |
| 752 | + [w3 w3'] (pair (op "ax3ay3")) |
| 753 | + [rx rx'] (pair (op "rx12")) |
| 754 | + [ry ry'] (pair (op "ry2")) |
| 755 | + h (history/index [w1 w2 w3 rx ry w1' w2' w3' rx' ry'])] |
| 756 | + ; w1 -ww-> w2, by rx12 |
| 757 | + ; w2 -ww-> w1, by ry2 |
| 758 | + ; ry -rw-> w1, since y fails to observe w1 |
| 759 | + ; w3 is a red herring; just there to create multiple final edges |
| 760 | + (is (= {:valid? false |
| 761 | + :anomaly-types [:G-single :G0] |
| 762 | + :anomalies |
| 763 | + ; We know this is G-single because ry -rw-> w1 -ww-> w2 -wr-> ry |
| 764 | + {:G-single |
| 765 | + [{:cycle |
| 766 | + [{:type :ok, :value [[:r :y [2]]], :index 9} |
| 767 | + {:type :ok, |
| 768 | + :value [[:append :x 1] [:append :y 1]], |
| 769 | + :index 5} |
| 770 | + {:type :ok, |
| 771 | + :value [[:append :x 2] [:append :y 2]], |
| 772 | + :index 6} |
| 773 | + {:type :ok, :value [[:r :y [2]]], :index 9}], |
| 774 | + :steps |
| 775 | + [{:type :rw, |
| 776 | + :key :y, |
| 777 | + :value 2, |
| 778 | + :value' 1, |
| 779 | + :a-mop-index 0, |
| 780 | + :b-mop-index 1} |
| 781 | + {:type :ww, |
| 782 | + :key :x, |
| 783 | + :value 1, |
| 784 | + :value' 2, |
| 785 | + :a-mop-index 0, |
| 786 | + :b-mop-index 0} |
| 787 | + {:type :wr, |
| 788 | + :key :y, |
| 789 | + :value 2, |
| 790 | + :a-mop-index 1, |
| 791 | + :b-mop-index 0}], |
| 792 | + :type :G-single}], |
| 793 | + ; But worse, it's G0 because w2 -ww-> w1 -ww->w2 |
| 794 | + :G0 |
| 795 | + [{:cycle |
| 796 | + [{:type :ok, |
| 797 | + :value [[:append :x 2] [:append :y 2]], |
| 798 | + :index 6} |
| 799 | + {:type :ok, |
| 800 | + :value [[:append :x 1] [:append :y 1]], |
| 801 | + :index 5} |
| 802 | + {:type :ok, |
| 803 | + :value [[:append :x 2] [:append :y 2]], |
| 804 | + :index 6}], |
| 805 | + :steps |
| 806 | + [{:type :ww, |
| 807 | + :key :y, |
| 808 | + :value 2, |
| 809 | + :value' 1, |
| 810 | + :a-mop-index 1, |
| 811 | + :b-mop-index 1} |
| 812 | + {:type :ww, |
| 813 | + :key :x, |
| 814 | + :value 1, |
| 815 | + :value' 2, |
| 816 | + :a-mop-index 0, |
| 817 | + :b-mop-index 0}], |
| 818 | + :type :G0}]}, |
| 819 | + :not #{:read-uncommitted}} |
| 820 | + (-> (c {:consistency-models [:serializable]} h) |
| 821 | + (dissoc :also-not)))))) |
| 822 | + |
711 | 823 | (deftest repeatable-read-test
|
712 | 824 | ; This is a long fork, which is also G2-item, by virtue of its only cycle
|
713 | 825 | ; being two anti-dependency edges. We shouldn't be able to detect this with
|
|
0 commit comments