/* * sophia database * sphia.org * * Copyright (c) Dmitry Simonenko * BSD License */ /* hermitage: Testing transaction isolation levels. github.com/ept/hermitage Sophia uses own implementation of SSI (Serialized Snapshot Isolation). */ #include #include #include #include #include #include #include static inline void set(void *dest, uint32_t id, uint32_t value) { void *o = st_document(id, value); t( sp_set(dest, o) == 0 ); } static inline void delete(void *dest, uint32_t id) { void *o = st_document(id, id); t( sp_delete(dest, o) == 0 ); } static inline void get(void *dest, uint32_t id, int value_to_check) { void *o = st_document(id, id); o = sp_get(dest, o); if (o == NULL) { t( value_to_check == -1 ); return; } st_document_is(o, id, value_to_check); sp_destroy(o); } static inline void* begin(void) { void *T = sp_begin(st_r.env); t( T != NULL ); return T; } static inline void commit(void *dest, int result) { t( sp_commit(dest) == result ); st_phase(); } static inline void rollback(void *dest) { t( sp_destroy(dest) == 0 ); st_phase(); } static void hermitage_g0(void) { set(st_r.db, 1, 10); set(st_r.db, 2, 20); void *T1 = begin(); void *T2 = begin(); set(T1, 1, 11); set(T2, 1, 12); set(T1, 2, 21); commit(T1, 0); set(T2, 2, 22); commit(T2, 1); /* conflict */ get(st_r.db, 1, 11); get(st_r.db, 2, 21); } static void hermitage_g1a(void) { set(st_r.db, 1, 10); set(st_r.db, 2, 20); void *T1 = begin(); void *T2 = begin(); set(T1, 1, 101); get(T2, 1, 10); rollback(T1); get(T2, 1, 10); commit(T2, 0); } static void hermitage_g1b(void) { set(st_r.db, 1, 10); set(st_r.db, 2, 20); void *T1 = begin(); void *T2 = begin(); set(T1, 1, 101); get(T2, 1, 10); set(T1, 1, 11); commit(T1, 0); get(T2, 1, 10); commit(T2, 0); /* T1(1) <- T2(1), but T2 is read only */ } static void hermitage_g1c(void) { set(st_r.db, 1, 10); set(st_r.db, 2, 20); void *T1 = begin(); void *T2 = begin(); set(T1, 1, 11); set(T2, 2, 22); get(T1, 2, 20); get(T2, 1, 10); commit(T1, 0); commit(T2, 0); } static void hermitage_otv(void) { /* observer transaction vanishes */ set(st_r.db, 1, 10); set(st_r.db, 2, 20); void *T1 = begin(); void *T2 = begin(); void *T3 = begin(); set(T1, 1, 11); set(T1, 2, 19); set(T2, 1, 12); commit(T1, 0); get(T3, 1, 10); /* snapshot created on begin */ set(T2, 2, 18); get(T3, 2, 20); commit(T2, 1); /* rollback on conflict */ get(T3, 2, 20); /* transaction not sees other updates */ get(T3, 1, 10); commit(T3, 0); } static void hermitage_pmp(void) { /* predicate-many-preceders */ set(st_r.db, 1, 10); set(st_r.db, 2, 20); void *T1 = begin(); void *T2 = begin(); /* select * from test where value = 30 */ void *cursor = sp_cursor(st_r.env); void *o = sp_document(st_r.db); while ((o = sp_get(cursor, o))) { uint32_t key = *(uint32_t*)sp_getstring(o, "key", NULL); t( key != 30 ); } sp_destroy(cursor); set(T2, 3, 30); commit(T2, 0); get(T1, 1, 10); get(T1, 2, 20); get(T1, 3, -1); commit(T1, 0); } static void hermitage_pmp_write(void) { /* predicate-many-preceders */ set(st_r.db, 1, 10); set(st_r.db, 2, 20); void *T1 = begin(); void *T2 = begin(); set(T1, 1, 20); set(T1, 2, 30); get(T2, 1, 10); get(T2, 2, 20); delete(T2, 2); commit(T1, 0); get(T2, 1, 10); commit(T2, 1); /* conflict */ get(st_r.db, 1, 20); set(st_r.db, 2, 30); } static void hermitage_p4(void) { /* lost update */ set(st_r.db, 1, 10); set(st_r.db, 2, 20); void *T1 = begin(); void *T2 = begin(); get(T1, 1, 10); get(T2, 1, 10); set(T1, 1, 11); set(T2, 1, 11); commit(T1, 0); commit(T2, 1); /* conflict */ } static void hermitage_g_single(void) { /* read-skew */ set(st_r.db, 1, 10); set(st_r.db, 2, 20); void *T1 = begin(); void *T2 = begin(); get(T1, 1, 10); get(T2, 1, 10); get(T2, 2, 20); set(T2, 1, 12); set(T2, 2, 18); commit(T2, 0); get(T1, 2, 20); commit(T1, 0); } static void hermitage_g2_item(void) { /* write-skew */ set(st_r.db, 1, 10); set(st_r.db, 2, 20); void *T1 = begin(); void *T2 = begin(); get(T1, 1, 10); get(T1, 2, 20); get(T2, 1, 10); get(T2, 2, 20); set(T1, 1, 11); set(T2, 1, 21); commit(T1, 0); commit(T2, 1); /* conflict */ } static void hermitage_g2(void) { /* anti-dependency cycles */ set(st_r.db, 1, 10); set(st_r.db, 2, 20); void *T1 = begin(); void *T2 = begin(); /* select * from test where value % 3 = 0 */ get(T1, 1, 10); get(T1, 2, 20); get(T2, 1, 10); get(T2, 2, 20); set(T1, 3, 30); set(T2, 4, 42); commit(T1, 0); commit(T2, 1); /* conflict */ } static void hermitage_g2_two_edges0(void) { /* anti-dependency cycles */ set(st_r.db, 1, 10); set(st_r.db, 2, 20); void *T1 = begin(); /* select * from test */ get(T1, 1, 10); get(T1, 2, 20); void *T2 = begin(); set(T2, 2, 25); commit(T2, 0); void *T3 = begin(); get(T3, 1, 10); get(T3, 2, 25); commit(T3, 0); set(T1, 1, 0); commit(T1, 1); } static void hermitage_g2_two_edges1(void) { /* anti-dependency cycles */ set(st_r.db, 1, 10); set(st_r.db, 2, 20); void *T1 = begin(); /* select * from test */ get(T1, 1, 10); get(T1, 2, 20); void *T2 = begin(); set(T2, 2, 25); commit(T2, 0); void *T3 = begin(); get(T3, 1, 10); get(T3, 2, 25); commit(T3, 0); /*set(T1, 1, 0);*/ commit(T1, 0); } stgroup *hermitage_group(void) { stgroup *group = st_group("hermitage"); st_groupadd(group, st_test("g0", hermitage_g0)); st_groupadd(group, st_test("g1a", hermitage_g1a)); st_groupadd(group, st_test("g1b", hermitage_g1b)); st_groupadd(group, st_test("g1c", hermitage_g1c)); st_groupadd(group, st_test("otv", hermitage_otv)); st_groupadd(group, st_test("pmp", hermitage_pmp)); st_groupadd(group, st_test("pmp-write", hermitage_pmp_write)); st_groupadd(group, st_test("p4", hermitage_p4)); st_groupadd(group, st_test("g-single", hermitage_g_single)); st_groupadd(group, st_test("g2-item", hermitage_g2_item)); st_groupadd(group, st_test("g2", hermitage_g2)); st_groupadd(group, st_test("g2_two_edges0", hermitage_g2_two_edges0)); st_groupadd(group, st_test("g2_two_edges1", hermitage_g2_two_edges1)); return group; }