@@ -45,6 +45,15 @@ def sync_left_to_right(l, r, since):
4545 r .commit ()
4646
4747
48+ def sync_left_to_right_exact_version (l , r , db_version ):
49+ changes = l .execute (
50+ "SELECT * FROM crsql_changes WHERE db_version = ?" , (db_version ,))
51+ for change in changes :
52+ r .execute (
53+ "INSERT INTO crsql_changes VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" , change )
54+ r .commit ()
55+
56+
4857def sync_left_to_right_include_siteid (l , r , since ):
4958 changes = l .execute (
5059 "SELECT [table], pk, cid, val, col_version, db_version, coalesce(site_id, crsql_site_id()), cl, seq FROM crsql_changes WHERE db_version > ?" , (since ,))
@@ -895,6 +904,96 @@ def test_pko_resurrect():
895904 close (c2 )
896905
897906
907+ # The case is:
908+ # Nodes A, B and C all have the same row
909+ # C deletes it
910+ # A receives the delete
911+ # B receives the delete
912+ # C re-inserts it
913+ # B receive the re-insertion and all cols for that re-insert
914+ # A receives nothing
915+ # C updates a few columns
916+ # A receives the column updates
917+ # B receives the column updates
918+ # A now receives the re-inesrtion and all colls for that re-insert
919+ # Everyone should be converged. In the user report,
920+ # A lost the values from the update.
921+ # https://discord.com/channels/989870439897653248/1145793546788544542/1146199296019009576
922+ def test_discord_report_corrosion ():
923+ changes_q = "SELECT * FROM crsql_changes"
924+
925+ def make_schema ():
926+ c = connect (":memory:" )
927+ c .execute ("CREATE TABLE foo (a primary key, b, c, d, e)" )
928+ c .execute ("SELECT crsql_as_crr('foo')" )
929+ c .commit ()
930+ return c
931+
932+ a = make_schema ()
933+ b = make_schema ()
934+ c = make_schema ()
935+
936+ c .execute ("INSERT INTO foo VALUES (1, 'b', 'c', 'd', 'e')" )
937+ c .commit ()
938+ sync_left_to_right_single_vrsn (c , a , 1 )
939+ sync_left_to_right_single_vrsn (c , b , 1 )
940+
941+ # all dbs in same state
942+ # c now does the delete
943+ c .execute ("DELETE FROM foo WHERE a = 1" )
944+ c .commit ()
945+
946+ # that delete now goes to peers
947+ sync_left_to_right_single_vrsn (c , a , 2 )
948+ sync_left_to_right_single_vrsn (c , b , 2 )
949+
950+ # c now re-inserts
951+ c .execute ("INSERT INTO foo VALUES (1, 'b1', 'c1', 'd1', 'e1')" )
952+ c .commit ()
953+
954+ # b gets the re-insertion
955+ sync_left_to_right_single_vrsn (c , b , 3 )
956+
957+ # c makes updates
958+ c .execute ("UPDATE foo SET b = 'b2', c = 'c2' WHERE a = 1" )
959+ c .commit ()
960+
961+ # those updates go to A and B
962+ sync_left_to_right_exact_version (c , a , 4 )
963+ sync_left_to_right_exact_version (c , b , 4 )
964+
965+ # make sure we have the expected change coming out of node C
966+ assert (c .execute (
967+ "SELECT cid, col_version, cl FROM crsql_changes WHERE db_version = 4" ).fetchall () ==
968+ [('b' , 2 , 3 ), ('c' , 2 , 3 )])
969+
970+ # a received the delete followed by updates of cells `b` and `c`
971+ # so it's changes should only have:
972+ # 1. the sentinal with causal length 3
973+ # 2. cell b with col version 2 and causal length 3
974+ # 3. cell c with the same
975+ assert (a .execute ("SELECT cid, col_version, cl FROM crsql_changes" ).fetchall (
976+ ) == [('-1' , 3 , 3 ), ('b' , 2 , 3 ), ('c' , 2 , 3 )])
977+
978+ # now that old re-insert goes to A
979+ sync_left_to_right_single_vrsn (c , a , 3 )
980+
981+ # nodes have the same final tables
982+ q = "SELECT * FROM foo"
983+ assert (a .execute (q ).fetchall () ==
984+ b .execute (q ).fetchall ())
985+
986+ # all nodes should have the same:
987+ # 1. column versions
988+ # 2. causal length
989+ # 3. value
990+ # db_versions could be different as changes are applied in different
991+ # orders on each node.
992+ q = "SELECT cid, col_version, cl, val FROM crsql_changes ORDER BY cid"
993+ assert (a .execute (q ).fetchall () == b .execute (q ).fetchall ())
994+ assert (a .execute (q ).fetchall () == c .execute (q ).fetchall ())
995+
996+
898997def test_cl_does_not_move_forward_when_equal ():
899998 None
900999
0 commit comments