Merge lp:~thisfred/u1db/simplify into lp:u1db

Proposed by Eric Casteleijn
Status: Merged
Approved by: Eric Casteleijn
Approved revision: 345
Merged at revision: 346
Proposed branch: lp:~thisfred/u1db/simplify
Merge into: lp:u1db
Diff against target: 623 lines (+155/-132)
11 files modified
include/u1db/u1db_internal.h (+7/-3)
src/u1db.c (+54/-38)
src/u1db_sync_target.c (+5/-4)
u1db/__init__.py (+1/-1)
u1db/backends/__init__.py (+18/-11)
u1db/backends/inmemory.py (+8/-8)
u1db/backends/sqlite_backend.py (+5/-6)
u1db/sync.py (+5/-6)
u1db/tests/c_backend_wrapper.pyx (+17/-16)
u1db/tests/test_backends.py (+8/-35)
u1db/tests/test_sync.py (+27/-4)
To merge this branch: bzr merge lp:~thisfred/u1db/simplify
Reviewer Review Type Date Requested Status
Samuele Pedroni Approve
Review via email: mp+113783@code.launchpad.net

Commit message

Simplified the checking of generations/transaction ids during sync. Invalid values now always result in errors, no autorecovery is attempted, and the validators do less work.

Description of the change

Simplified the checking of generations/transaction ids during sync. Invalid values now always result in errors, no autorecovery is attempted, and the validators do less work.

To post a comment you must log in.
Revision history for this message
Samuele Pedroni (pedronis) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/u1db/u1db_internal.h'
2--- include/u1db/u1db_internal.h 2012-07-03 15:48:59 +0000
3+++ include/u1db/u1db_internal.h 2012-07-06 17:55:21 +0000
4@@ -223,9 +223,7 @@
5 * superseded.
6 */
7 int u1db__validate_source(u1database *db, const char *replica_uid,
8- int replica_gen, const char *replica_trans_id,
9- u1db_vectorclock *cur_vcr,
10- u1db_vectorclock *other_vcr, int *state);
11+ int replica_gen, const char *replica_trans_id);
12
13 /**
14 * Internal API, Get the global database rev.
15@@ -239,6 +237,12 @@
16 char **trans_id);
17
18 /**
19+ * Internal API, Get the transaction id for the db generation.
20+ */
21+int u1db__get_trans_id_for_gen(u1database *db, int generation,
22+ char **trans_id);
23+
24+/**
25 * Internal API, Validate generation and transaction id.
26 */
27 int u1db_validate_gen_and_trans_id(u1database *db, int generation,
28
29=== modified file 'src/u1db.c'
30--- src/u1db.c 2012-07-03 15:48:59 +0000
31+++ src/u1db.c 2012-07-06 17:55:21 +0000
32@@ -42,7 +42,6 @@
33 char **doc_rev);
34 static int generate_transaction_id(char buf[35]);
35
36-
37 static int
38 initialize(u1database *db)
39 {
40@@ -756,23 +755,17 @@
41
42 int
43 u1db__validate_source(u1database *db, const char *replica_uid, int replica_gen,
44- const char *replica_trans_id, u1db_vectorclock *cur,
45- u1db_vectorclock *other, int *state)
46+ const char *replica_trans_id)
47 {
48 int old_generation;
49 char *old_trans_id = NULL;
50 int status = U1DB_OK;
51
52- *state = U1DB_OK;
53 status = u1db__get_replica_gen_and_trans_id(
54 db, replica_uid, &old_generation, &old_trans_id);
55 if (status != U1DB_OK)
56 goto finish;
57 if (replica_gen < old_generation) {
58- if (u1db__vectorclock_is_newer(cur, other)) {
59- *state = U1DB_SUPERSEDED;
60- goto finish;
61- }
62 status = U1DB_INVALID_GENERATION;
63 goto finish;
64 }
65@@ -782,7 +775,6 @@
66 status = U1DB_INVALID_TRANSACTION_ID;
67 goto finish;
68 }
69- *state = U1DB_SUPERSEDED;
70 finish:
71 if (old_trans_id != NULL)
72 free(old_trans_id);
73@@ -830,15 +822,10 @@
74 }
75 if (replica_uid != NULL && replica_trans_id != NULL) {
76 status = u1db__validate_source(
77- db, replica_uid, replica_gen, replica_trans_id, stored_vc, new_vc,
78- state);
79+ db, replica_uid, replica_gen, replica_trans_id);
80 if (status != U1DB_OK) {
81 goto finish;
82 }
83- if (*state != U1DB_OK) {
84- status = u1db__get_generation(db, at_gen);
85- goto finish;
86- }
87 }
88 if (stored_doc_rev == NULL) {
89 status = U1DB_OK;
90@@ -1469,9 +1456,51 @@
91 *trans_id = strdup(tmp);
92 if (*trans_id == NULL) {
93 status = U1DB_NOMEM;
94- }
95- }
96- }
97+ goto finish;
98+ }
99+ }
100+ status = U1DB_OK;
101+ }
102+finish:
103+ sqlite3_finalize(statement);
104+ return status;
105+}
106+
107+int
108+u1db__get_trans_id_for_gen(u1database *db, int generation,
109+ char **trans_id)
110+{
111+ int status = U1DB_OK;
112+ sqlite3_stmt *statement;
113+ const char *tmp;
114+
115+ if (db == NULL) {
116+ return U1DB_INVALID_PARAMETER;
117+ }
118+ status = sqlite3_prepare_v2(db->sql_handle,
119+ "SELECT transaction_id FROM transaction_log WHERE generation = ?", -1,
120+ &statement, NULL);
121+ if (status != SQLITE_OK) { goto finish; }
122+ status = sqlite3_bind_int(statement, 1, generation);
123+ if (status != SQLITE_OK) { goto finish; }
124+ status = sqlite3_step(statement);
125+ if (status == SQLITE_DONE) {
126+ status = U1DB_INVALID_GENERATION;
127+ goto finish;
128+ } else if (status == SQLITE_ROW) {
129+ tmp = (const char *)sqlite3_column_text(statement, 0);
130+ if (tmp == NULL) {
131+ *trans_id = NULL;
132+ } else {
133+ *trans_id = strdup(tmp);
134+ if (*trans_id == NULL) {
135+ status = U1DB_NOMEM;
136+ goto finish;
137+ }
138+ }
139+ status = U1DB_OK;
140+ }
141+finish:
142 sqlite3_finalize(statement);
143 return status;
144 }
145@@ -1481,34 +1510,21 @@
146 const char *trans_id)
147 {
148 int status = U1DB_OK;
149- sqlite3_stmt *statement;
150+ char *known_trans_id = NULL;
151
152 if (generation == 0)
153 return status;
154- if (db == NULL) {
155- return U1DB_INVALID_PARAMETER;
156- }
157- status = sqlite3_prepare_v2(db->sql_handle,
158- "SELECT transaction_id FROM transaction_log WHERE generation = ?", -1,
159- &statement, NULL);
160- if (status != SQLITE_OK) { goto finish; }
161- status = sqlite3_bind_int(statement, 1, generation);
162- if (status != SQLITE_OK) { goto finish; }
163- status = sqlite3_step(statement);
164- if (status == SQLITE_DONE) {
165- status = U1DB_INVALID_GENERATION;
166+ status = u1db__get_trans_id_for_gen(db, generation, &known_trans_id);
167+ if (status != U1DB_OK)
168 goto finish;
169- } else if (status == SQLITE_ROW) {
170- // Note: We may want to handle the column containing NULL
171- if (strcmp(trans_id,
172- (const char *)sqlite3_column_text(statement, 0)) == 0) {
173+ if (strcmp(trans_id, known_trans_id) == 0) {
174 status = U1DB_OK;
175 goto finish;
176 }
177- status = U1DB_INVALID_TRANSACTION_ID;
178- }
179+ status = U1DB_INVALID_TRANSACTION_ID;
180 finish:
181- sqlite3_finalize(statement);
182+ if (known_trans_id != NULL)
183+ free(known_trans_id);
184 return status;
185 }
186
187
188=== modified file 'src/u1db_sync_target.c'
189--- src/u1db_sync_target.c 2012-07-03 15:48:59 +0000
190+++ src/u1db_sync_target.c 2012-07-06 17:55:21 +0000
191@@ -105,8 +105,8 @@
192
193 static int
194 st_get_sync_info(u1db_sync_target *st, const char *source_replica_uid,
195- const char **st_replica_uid, int *st_gen, int *source_gen,
196- char **source_trans_id)
197+ const char **st_replica_uid, int *st_gen, int *source_gen,
198+ char **source_trans_id)
199 {
200 int status = U1DB_OK;
201 u1database *db;
202@@ -614,8 +614,9 @@
203 status = u1db_validate_gen_and_trans_id(
204 db, local_gen_known_by_target, local_trans_id_known_by_target);
205 if (status != U1DB_OK) { goto finish; }
206- status = u1db__get_replica_gen_and_trans_id(db, target_uid,
207- &target_gen_known_by_local, &target_trans_id_known_by_local);
208+ status = u1db__get_replica_gen_and_trans_id(
209+ db, target_uid, &target_gen_known_by_local,
210+ &target_trans_id_known_by_local);
211 if (status != U1DB_OK) { goto finish; }
212 local_target_trans_id = target_trans_id_known_by_local;
213 local_gen = local_gen_known_by_target;
214
215=== modified file 'u1db/__init__.py'
216--- u1db/__init__.py 2012-07-03 15:48:59 +0000
217+++ u1db/__init__.py 2012-07-06 17:55:21 +0000
218@@ -290,7 +290,7 @@
219 encountered during synchronization. If we've never synchronized
220 with the replica, this is (0, '').
221 """
222- raise NotImplementedError(self._replica_gen_and_trans_id)
223+ raise NotImplementedError(self._get_replica_gen_and_trans_id)
224
225 def _set_replica_gen_and_trans_id(self, other_replica_uid,
226 other_generation, other_transaction_id):
227
228=== modified file 'u1db/backends/__init__.py'
229--- u1db/backends/__init__.py 2012-07-03 13:50:27 +0000
230+++ u1db/backends/__init__.py 2012-07-06 17:55:21 +0000
231@@ -102,6 +102,14 @@
232 result.append(doc)
233 return result
234
235+ def _get_trans_id_for_gen(self, generation):
236+ """Get the transaction id corresponding to a particular generation.
237+
238+ Raises an InvalidGeneration when the generation does not exist.
239+
240+ """
241+ raise NotImplementedError(self._get_trans_id_for_gen)
242+
243 def validate_gen_and_trans_id(self, generation, trans_id):
244 """Validate the generation and transaction id.
245
246@@ -109,10 +117,14 @@
247 InvalidTransactionId when it does but with a different transaction id.
248
249 """
250- raise NotImplementedError(self.validate_gen_and_trans_id)
251+ if generation == 0:
252+ return
253+ known_trans_id = self._get_trans_id_for_gen(generation)
254+ if known_trans_id != trans_id:
255+ raise errors.InvalidTransactionId
256
257 def _validate_source(self, other_replica_uid, other_generation,
258- other_transaction_id, cur_vcr, other_vcr):
259+ other_transaction_id):
260 """Validate the new generation and transaction id.
261
262 other_generation must be greater than what we have stored for this
263@@ -123,13 +135,11 @@
264 old_transaction_id) = self._get_replica_gen_and_trans_id(
265 other_replica_uid)
266 if other_generation < old_generation:
267- if cur_vcr.is_newer(other_vcr):
268- return 'superseded'
269 raise errors.InvalidGeneration
270 if other_generation > old_generation:
271- return 'ok'
272+ return
273 if other_transaction_id == old_transaction_id:
274- return 'superseded'
275+ return
276 raise errors.InvalidTransactionId
277
278 def _put_doc_if_newer(self, doc, save_conflict, replica_uid=None,
279@@ -141,11 +151,8 @@
280 else:
281 cur_vcr = VectorClockRev(cur_doc.rev)
282 if replica_uid is not None and replica_gen is not None:
283- state = self._validate_source(
284- replica_uid, replica_gen, replica_trans_id, cur_vcr,
285- doc_vcr)
286- if state != 'ok':
287- return state, self._get_generation()
288+ self._validate_source(
289+ replica_uid, replica_gen, replica_trans_id)
290 if doc_vcr.is_newer(cur_vcr):
291 rev = doc.rev
292 self._prune_conflicts(doc, doc_vcr)
293
294=== modified file 'u1db/backends/inmemory.py'
295--- u1db/backends/inmemory.py 2012-07-03 15:48:59 +0000
296+++ u1db/backends/inmemory.py 2012-07-06 17:55:21 +0000
297@@ -85,16 +85,16 @@
298 return len(self._transaction_log)
299
300 def _get_generation_info(self):
301+ if not self._transaction_log:
302+ return 0, ''
303 return len(self._transaction_log), self._transaction_log[-1][1]
304
305- def validate_gen_and_trans_id(self, generation, trans_id):
306+ def _get_trans_id_for_gen(self, generation):
307 if generation == 0:
308- return
309+ return ''
310 if generation > len(self._transaction_log):
311 raise errors.InvalidGeneration
312- if self._transaction_log[generation - 1][1] == trans_id:
313- return
314- raise errors.InvalidTransactionId
315+ return self._transaction_log[generation - 1][1]
316
317 def put_doc(self, doc):
318 if doc.doc_id is None:
319@@ -448,10 +448,10 @@
320 class InMemorySyncTarget(CommonSyncTarget):
321
322 def get_sync_info(self, source_replica_uid):
323- source_gen, trans_id = self._db._get_replica_gen_and_trans_id(
324+ source_gen, source_trans_id = self._db._get_replica_gen_and_trans_id(
325 source_replica_uid)
326- return (self._db._replica_uid, len(self._db._transaction_log),
327- source_gen, trans_id)
328+ my_gen = self._db._get_generation()
329+ return self._db._replica_uid, my_gen, source_gen, source_trans_id
330
331 def record_sync_info(self, source_replica_uid, source_replica_generation,
332 source_transaction_id):
333
334=== modified file 'u1db/backends/sqlite_backend.py'
335--- u1db/backends/sqlite_backend.py 2012-07-03 13:50:27 +0000
336+++ u1db/backends/sqlite_backend.py 2012-07-06 17:55:21 +0000
337@@ -274,9 +274,9 @@
338 return(0, val[1])
339 return val
340
341- def validate_gen_and_trans_id(self, generation, trans_id):
342+ def _get_trans_id_for_gen(self, generation):
343 if generation == 0:
344- return
345+ return ''
346 c = self._db_handle.cursor()
347 c.execute(
348 'SELECT transaction_id FROM transaction_log WHERE generation = ?',
349@@ -284,8 +284,7 @@
350 val = c.fetchone()
351 if val is None:
352 raise errors.InvalidGeneration
353- if val[0] != trans_id:
354- raise errors.InvalidTransactionId
355+ return val[0]
356
357 def _get_transaction_log(self):
358 c = self._db_handle.cursor()
359@@ -788,10 +787,10 @@
360 class SQLiteSyncTarget(CommonSyncTarget):
361
362 def get_sync_info(self, source_replica_uid):
363- source_gen, trans_id = self._db._get_replica_gen_and_trans_id(
364+ source_gen, source_trans_id = self._db._get_replica_gen_and_trans_id(
365 source_replica_uid)
366 my_gen = self._db._get_generation()
367- return self._db._replica_uid, my_gen, source_gen, trans_id
368+ return self._db._replica_uid, my_gen, source_gen, source_trans_id
369
370 def record_sync_info(self, source_replica_uid, source_replica_generation,
371 source_replica_transaction_id):
372
373=== modified file 'u1db/sync.py'
374--- u1db/sync.py 2012-07-03 15:48:59 +0000
375+++ u1db/sync.py 2012-07-06 17:55:21 +0000
376@@ -97,18 +97,17 @@
377 (self.target_replica_uid, target_gen, target_my_gen,
378 target_my_trans_id) = sync_target.get_sync_info(
379 self.source._replica_uid)
380- # validate that the generation and transaction id the target knows
381- # about us are valid.
382+ # validate the generation and transaction id the target knows about us
383 self.source.validate_gen_and_trans_id(
384 target_my_gen, target_my_trans_id)
385 # what's changed since that generation and this current gen
386 my_gen, _, changes = self.source.whats_changed(target_my_gen)
387
388 # this source last-seen database generation for the target
389- (target_last_known_gen,
390- target_trans_id) = self.source._get_replica_gen_and_trans_id(
391- self.target_replica_uid)
392+ target_last_known_gen, target_last_known_trans_id = \
393+ self.source._get_replica_gen_and_trans_id(self.target_replica_uid)
394 if not changes and target_last_known_gen == target_gen:
395+ # TODO: we'll need to check the target's transaction id as well.
396 return my_gen
397 changed_doc_ids = [doc_id for doc_id, _, _ in changes]
398 # prepare to send all the changed docs
399@@ -123,7 +122,7 @@
400 # the target, return target synced-up-to gen
401 new_gen, new_trans_id = sync_target.sync_exchange(
402 docs_by_generation, self.source._replica_uid,
403- target_last_known_gen, target_trans_id,
404+ target_last_known_gen, target_last_known_trans_id,
405 self._insert_doc_from_target)
406 # record target synced-up-to generation including applying what we sent
407 self.source._set_replica_gen_and_trans_id(
408
409=== modified file 'u1db/tests/c_backend_wrapper.pyx'
410--- u1db/tests/c_backend_wrapper.pyx 2012-07-03 15:48:59 +0000
411+++ u1db/tests/c_backend_wrapper.pyx 2012-07-06 17:55:21 +0000
412@@ -81,9 +81,7 @@
413 void *context, u1db_doc_callback cb)
414 int u1db_put_doc(u1database *db, u1db_document *doc)
415 int u1db__validate_source(u1database *db, const_char_ptr replica_uid,
416- int replica_gen, const_char_ptr replica_trans_id,
417- u1db_vectorclock *cur_vcr,
418- u1db_vectorclock *other_vcr, int *state)
419+ int replica_gen, const_char_ptr replica_trans_id)
420 int u1db__put_doc_if_newer(u1database *db, u1db_document *doc,
421 int save_conflict, char *replica_uid,
422 int replica_gen, char *replica_trans_id,
423@@ -206,6 +204,7 @@
424
425 int u1db__get_generation(u1database *, int *db_rev)
426 int u1db__get_generation_info(u1database *, int *db_rev, char **trans_id)
427+ int u1db__get_trans_id_for_gen(u1database *, int db_rev, char **trans_id)
428 int u1db_validate_gen_and_trans_id(u1database *, int db_rev,
429 const_char_ptr trans_id)
430 char *u1db__allocate_doc_id(u1database *)
431@@ -941,26 +940,16 @@
432 u1db_put_doc(self._db, doc._doc))
433 return doc.rev
434
435- def _validate_source(self, replica_uid, replica_gen, replica_trans_id,
436- cur_vcr, other_vcr):
437+ def _validate_source(self, replica_uid, replica_gen, replica_trans_id):
438 cdef const_char_ptr c_uid, c_trans_id
439- cdef int c_gen, state = 0
440- cdef VectorClockRev cur
441- cdef VectorClockRev other
442+ cdef int c_gen = 0
443
444- cur = VectorClockRev(cur_vcr.as_str())
445- other = VectorClockRev(other_vcr.as_str())
446 c_uid = replica_uid
447 c_trans_id = replica_trans_id
448 c_gen = replica_gen
449 handle_status(
450 "invalid generation or transaction id",
451- u1db__validate_source(
452- self._db, c_uid, c_gen, c_trans_id, cur._clock, other._clock,
453- &state))
454- if state == U1DB_SUPERSEDED:
455- return 'superseded'
456- return 'ok'
457+ u1db__validate_source(self._db, c_uid, c_gen, c_trans_id))
458
459 def _put_doc_if_newer(self, CDocument doc, save_conflict, replica_uid=None,
460 replica_gen=None, replica_trans_id=None):
461@@ -1100,6 +1089,18 @@
462 "validate_gen_and_trans_id",
463 u1db_validate_gen_and_trans_id(self._db, generation, trans_id))
464
465+ def _get_trans_id_for_gen(self, generation):
466+ cdef char *trans_id = NULL
467+
468+ handle_status(
469+ "_get_trans_id_for_gen",
470+ u1db__get_trans_id_for_gen(self._db, generation, &trans_id))
471+ raw_trans_id = None
472+ if trans_id != NULL:
473+ raw_trans_id = trans_id
474+ free(trans_id)
475+ return raw_trans_id
476+
477 def _get_replica_gen_and_trans_id(self, replica_uid):
478 cdef int generation, status
479 cdef char *trans_id = NULL
480
481=== modified file 'u1db/tests/test_backends.py'
482--- u1db/tests/test_backends.py 2012-07-04 15:43:00 +0000
483+++ u1db/tests/test_backends.py 2012-07-06 17:55:21 +0000
484@@ -402,11 +402,12 @@
485
486 def test_put_doc_if_newer_same_generation_same_txid(self):
487 self.db._set_replica_gen_and_trans_id('other', 1, 'T-sid')
488- doc = self.make_document('doc_id', 'other:2', simple_doc)
489+ doc = self.db.create_doc(simple_doc)
490+ doc2 = self.make_document(doc.doc_id, 'other:1', simple_doc)
491 state, _ = self.db._put_doc_if_newer(
492 doc, save_conflict=False, replica_uid='other', replica_gen=1,
493 replica_trans_id='T-sid')
494- self.assertEqual('superseded', state)
495+ self.assertEqual('converged', state)
496
497 def test_put_doc_if_newer_wrong_transaction_id(self):
498 self.db._set_replica_gen_and_trans_id('other', 1, 'T-sid')
499@@ -422,10 +423,10 @@
500 doc_rev1 = doc.rev
501 doc.set_json(simple_doc)
502 self.db.put_doc(doc)
503- self.db._set_replica_gen_and_trans_id('other', 5, 'T-sid')
504+ self.db._set_replica_gen_and_trans_id('other', 3, 'T-sid')
505 older_doc = self.make_document(doc.doc_id, doc_rev1, simple_doc)
506 state, _ = self.db._put_doc_if_newer(
507- older_doc, save_conflict=False, replica_uid='other', replica_gen=3,
508+ older_doc, save_conflict=False, replica_uid='other', replica_gen=8,
509 replica_trans_id='T-irrelevant')
510 self.assertEqual('superseded', state)
511
512@@ -557,45 +558,17 @@
513
514 def test_validate_source_gen_and_trans_id_same(self):
515 self.db._set_replica_gen_and_trans_id('other', 1, 'T-sid')
516- v1 = vectorclock.VectorClockRev('other:1|self:1')
517- v2 = vectorclock.VectorClockRev('other:1|self:1')
518- self.assertEqual(
519- 'superseded',
520- self.db._validate_source('other', 1, 'T-sid', v1, v2))
521+ self.db._validate_source('other', 1, 'T-sid')
522
523 def test_validate_source_gen_newer(self):
524 self.db._set_replica_gen_and_trans_id('other', 1, 'T-sid')
525- v1 = vectorclock.VectorClockRev('other:1|self:1')
526- v2 = vectorclock.VectorClockRev('other:2|self:2')
527- self.assertEqual(
528- 'ok',
529- self.db._validate_source('other', 2, 'T-whatevs', v1, v2))
530+ self.db._validate_source('other', 2, 'T-whatevs')
531
532 def test_validate_source_wrong_txid(self):
533 self.db._set_replica_gen_and_trans_id('other', 1, 'T-sid')
534- v1 = vectorclock.VectorClockRev('other:1|self:1')
535- v2 = vectorclock.VectorClockRev('other:2|self:2')
536 self.assertRaises(
537 errors.InvalidTransactionId,
538- self.db._validate_source, 'other', 1, 'T-sad', v1, v2)
539-
540- def test_validate_source_gen_older_and_vcr_older(self):
541- self.db._set_replica_gen_and_trans_id('other', 1, 'T-sid')
542- self.db._set_replica_gen_and_trans_id('other', 2, 'T-sod')
543- v1 = vectorclock.VectorClockRev('other:1|self:1')
544- v2 = vectorclock.VectorClockRev('other:2|self:2')
545- self.assertEqual(
546- 'superseded',
547- self.db._validate_source('other', 1, 'T-sid', v2, v1))
548-
549- def test_validate_source_gen_older_vcr_newer(self):
550- self.db._set_replica_gen_and_trans_id('other', 1, 'T-sid')
551- self.db._set_replica_gen_and_trans_id('other', 2, 'T-sod')
552- v1 = vectorclock.VectorClockRev('other:1|self:1')
553- v2 = vectorclock.VectorClockRev('other:2|self:2')
554- self.assertRaises(
555- errors.InvalidGeneration,
556- self.db._validate_source, 'other', 1, 'T-sid', v1, v2)
557+ self.db._validate_source, 'other', 1, 'T-sad')
558
559
560 class LocalDatabaseWithConflictsTests(tests.DatabaseBaseTests):
561
562=== modified file 'u1db/tests/test_sync.py'
563--- u1db/tests/test_sync.py 2012-07-04 16:03:39 +0000
564+++ u1db/tests/test_sync.py 2012-07-06 17:55:21 +0000
565@@ -424,7 +424,8 @@
566 return db
567
568
569-def sync_via_synchronizer_and_http(test, db_source, db_target, trace_hook=None):
570+def sync_via_synchronizer_and_http(test, db_source, db_target,
571+ trace_hook=None):
572 if trace_hook:
573 test.skipTest("trace_hook unsupported over http")
574 path = test._http_at[db_target]
575@@ -988,6 +989,27 @@
576 self.assertRaises(
577 errors.InvalidTransactionId, self.sync, self.db1, db3)
578
579+ def test_sync_detects_rollback_and_divergence_in_source(self):
580+ self.db1.create_doc(tests.simple_doc, doc_id="divergent")
581+ self.sync(self.db1, self.db2)
582+ self.db1.create_doc(tests.simple_doc)
583+ self.db2._set_replica_gen_and_trans_id(
584+ self.db1._replica_uid, 2, 'T-madeup')
585+ self.assertRaises(
586+ errors.InvalidTransactionId, self.sync, self.db1, self.db2)
587+
588+ def test_sync_detects_rollback_and_divergence_in_target(self):
589+ # TODO: reenable this once we check the trans_id in Synchronizer.sync
590+ self.skip("TODO: check target_trans_id")
591+ self.db1.create_doc(tests.simple_doc, doc_id="divergent")
592+ self.sync(self.db1, self.db2)
593+ self.db2.create_doc(tests.simple_doc)
594+ self.db1._set_replica_gen_and_trans_id(
595+ self.db2._replica_uid, 2, 'T-madeup')
596+ self.sync(self.db1, self.db2)
597+ self.assertRaises(
598+ errors.InvalidTransactionId, self.sync, self.db1, self.db2)
599+
600
601 class TestDbSync(tests.TestCaseWithServer):
602 """Test db.sync remote sync shortcut"""
603@@ -1032,7 +1054,8 @@
604 self.assertEqual(2, len(self.db2._get_transaction_log()))
605 progress1 = []
606 progress2 = []
607- _do_set_replica_gen_and_trans_id = self.db1._do_set_replica_gen_and_trans_id
608+ _do_set_replica_gen_and_trans_id = \
609+ self.db1._do_set_replica_gen_and_trans_id
610
611 def set_sync_generation_witness1(other_uid, other_gen, trans_id):
612 progress1.append((other_uid, other_gen,
613@@ -1040,8 +1063,8 @@
614 _do_set_replica_gen_and_trans_id(other_uid, other_gen, trans_id)
615 self.patch(self.db1, '_do_set_replica_gen_and_trans_id',
616 set_sync_generation_witness1)
617-
618- _do_set_replica_gen_and_trans_id2 = self.db2._do_set_replica_gen_and_trans_id
619+ _do_set_replica_gen_and_trans_id2 = \
620+ self.db2._do_set_replica_gen_and_trans_id
621
622 def set_sync_generation_witness2(other_uid, other_gen, trans_id):
623 progress2.append((other_uid, other_gen,

Subscribers

People subscribed via source and target branches