Merge lp:~thisfred/u1db/txid_in_get_sync_info into lp:u1db
- txid_in_get_sync_info
- Merge into trunk
Proposed by
Eric Casteleijn
Status: | Superseded |
---|---|
Proposed branch: | lp:~thisfred/u1db/txid_in_get_sync_info |
Merge into: | lp:u1db |
Diff against target: |
1001 lines (+240/-172) 18 files modified
include/u1db/u1db_internal.h (+14/-8) src/u1db.c (+55/-35) src/u1db_http_sync_target.c (+23/-7) src/u1db_sync_target.c (+18/-9) u1db/__init__.py (+2/-2) u1db/backends/__init__.py (+18/-11) u1db/backends/inmemory.py (+10/-8) u1db/backends/sqlite_backend.py (+9/-8) u1db/remote/http_app.py (+3/-2) u1db/remote/http_target.py (+1/-0) u1db/sync.py (+8/-7) u1db/tests/c_backend_wrapper.pyx (+30/-22) u1db/tests/test_backends.py (+8/-35) u1db/tests/test_c_backend.py (+1/-1) u1db/tests/test_http_app.py (+3/-1) u1db/tests/test_remote_sync_target.py (+1/-1) u1db/tests/test_sqlite_backend.py (+1/-1) u1db/tests/test_sync.py (+35/-14) |
To merge this branch: | bzr merge lp:~thisfred/u1db/txid_in_get_sync_info |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu One hackers | Pending | ||
Review via email: mp+113801@code.launchpad.net |
This proposal has been superseded by a proposal from 2012-07-06.
Commit message
Description of the change
Added the target transaction id to the return parameters of get_sync_info, since we need it in sync() to be able to tell if the target diverged if it has the generation we expect.
To post a comment you must log in.
Unmerged revisions
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 20:51:26 +0000 |
4 | @@ -62,23 +62,25 @@ |
5 | * Note that this is const char and memory will be |
6 | * managed by the sync_target, so it should *not* |
7 | * be freed. |
8 | - * @param st_get (OUT) The database generation for this sync |
9 | + * @param st_gen (OUT) The database generation for this sync |
10 | + * target, matches st_replica_uid |
11 | + * @param st_trans_id (OUT) The database transaction id for this sync |
12 | * target, matches st_replica_uid |
13 | * @param source_gen (OUT) The last generation of source_replica_uid |
14 | * that st has synchronized with. |
15 | - * @param trans_id (OUT) The transaction id associated with the |
16 | + * @param source_trans_id (OUT) The transaction id associated with the |
17 | * source generation, the memory must be freed by |
18 | * the caller. |
19 | */ |
20 | int (*get_sync_info)(u1db_sync_target *st, |
21 | const char *source_replica_uid, |
22 | - const char **st_replica_uid, int *st_gen, int *source_gen, |
23 | - char **trans_id); |
24 | + const char **st_replica_uid, int *st_gen, char **st_trans_id, |
25 | + int *source_gen, char **source_trans_id); |
26 | /** |
27 | * Set the synchronization information about another replica. |
28 | * |
29 | * @param st Pass this sync_target to the function, |
30 | - * eg st->get_sync_info(st, ...) |
31 | + * eg st->record_sync_info(st, ...) |
32 | * @param source_replica_uid The unique identifier for the source we |
33 | * want to synchronize from. |
34 | * @param source_gen The last generation of source_replica_uid |
35 | @@ -223,9 +225,7 @@ |
36 | * superseded. |
37 | */ |
38 | int u1db__validate_source(u1database *db, const char *replica_uid, |
39 | - int replica_gen, const char *replica_trans_id, |
40 | - u1db_vectorclock *cur_vcr, |
41 | - u1db_vectorclock *other_vcr, int *state); |
42 | + int replica_gen, const char *replica_trans_id); |
43 | |
44 | /** |
45 | * Internal API, Get the global database rev. |
46 | @@ -239,6 +239,12 @@ |
47 | char **trans_id); |
48 | |
49 | /** |
50 | + * Internal API, Get the transaction id for the db generation. |
51 | + */ |
52 | +int u1db__get_trans_id_for_gen(u1database *db, int generation, |
53 | + char **trans_id); |
54 | + |
55 | +/** |
56 | * Internal API, Validate generation and transaction id. |
57 | */ |
58 | int u1db_validate_gen_and_trans_id(u1database *db, int generation, |
59 | |
60 | === modified file 'src/u1db.c' |
61 | --- src/u1db.c 2012-07-03 15:48:59 +0000 |
62 | +++ src/u1db.c 2012-07-06 20:51:26 +0000 |
63 | @@ -42,7 +42,6 @@ |
64 | char **doc_rev); |
65 | static int generate_transaction_id(char buf[35]); |
66 | |
67 | - |
68 | static int |
69 | initialize(u1database *db) |
70 | { |
71 | @@ -756,23 +755,17 @@ |
72 | |
73 | int |
74 | u1db__validate_source(u1database *db, const char *replica_uid, int replica_gen, |
75 | - const char *replica_trans_id, u1db_vectorclock *cur, |
76 | - u1db_vectorclock *other, int *state) |
77 | + const char *replica_trans_id) |
78 | { |
79 | int old_generation; |
80 | char *old_trans_id = NULL; |
81 | int status = U1DB_OK; |
82 | |
83 | - *state = U1DB_OK; |
84 | status = u1db__get_replica_gen_and_trans_id( |
85 | db, replica_uid, &old_generation, &old_trans_id); |
86 | if (status != U1DB_OK) |
87 | goto finish; |
88 | if (replica_gen < old_generation) { |
89 | - if (u1db__vectorclock_is_newer(cur, other)) { |
90 | - *state = U1DB_SUPERSEDED; |
91 | - goto finish; |
92 | - } |
93 | status = U1DB_INVALID_GENERATION; |
94 | goto finish; |
95 | } |
96 | @@ -782,7 +775,6 @@ |
97 | status = U1DB_INVALID_TRANSACTION_ID; |
98 | goto finish; |
99 | } |
100 | - *state = U1DB_SUPERSEDED; |
101 | finish: |
102 | if (old_trans_id != NULL) |
103 | free(old_trans_id); |
104 | @@ -830,15 +822,10 @@ |
105 | } |
106 | if (replica_uid != NULL && replica_trans_id != NULL) { |
107 | status = u1db__validate_source( |
108 | - db, replica_uid, replica_gen, replica_trans_id, stored_vc, new_vc, |
109 | - state); |
110 | + db, replica_uid, replica_gen, replica_trans_id); |
111 | if (status != U1DB_OK) { |
112 | goto finish; |
113 | } |
114 | - if (*state != U1DB_OK) { |
115 | - status = u1db__get_generation(db, at_gen); |
116 | - goto finish; |
117 | - } |
118 | } |
119 | if (stored_doc_rev == NULL) { |
120 | status = U1DB_OK; |
121 | @@ -1464,14 +1451,60 @@ |
122 | *generation = sqlite3_column_int(statement, 0); |
123 | tmp = (const char *)sqlite3_column_text(statement, 1); |
124 | if (tmp == NULL) { |
125 | + *trans_id = strdup(""); |
126 | + if (*trans_id == NULL) { |
127 | + status = U1DB_NOMEM; |
128 | + goto finish; |
129 | + } |
130 | + } else { |
131 | + *trans_id = strdup(tmp); |
132 | + if (*trans_id == NULL) { |
133 | + status = U1DB_NOMEM; |
134 | + goto finish; |
135 | + } |
136 | + } |
137 | + status = U1DB_OK; |
138 | + } |
139 | +finish: |
140 | + sqlite3_finalize(statement); |
141 | + return status; |
142 | +} |
143 | + |
144 | +int |
145 | +u1db__get_trans_id_for_gen(u1database *db, int generation, |
146 | + char **trans_id) |
147 | +{ |
148 | + int status = U1DB_OK; |
149 | + sqlite3_stmt *statement; |
150 | + const char *tmp; |
151 | + |
152 | + if (db == NULL) { |
153 | + return U1DB_INVALID_PARAMETER; |
154 | + } |
155 | + status = sqlite3_prepare_v2(db->sql_handle, |
156 | + "SELECT transaction_id FROM transaction_log WHERE generation = ?", -1, |
157 | + &statement, NULL); |
158 | + if (status != SQLITE_OK) { goto finish; } |
159 | + status = sqlite3_bind_int(statement, 1, generation); |
160 | + if (status != SQLITE_OK) { goto finish; } |
161 | + status = sqlite3_step(statement); |
162 | + if (status == SQLITE_DONE) { |
163 | + status = U1DB_INVALID_GENERATION; |
164 | + goto finish; |
165 | + } else if (status == SQLITE_ROW) { |
166 | + tmp = (const char *)sqlite3_column_text(statement, 0); |
167 | + if (tmp == NULL) { |
168 | *trans_id = NULL; |
169 | } else { |
170 | *trans_id = strdup(tmp); |
171 | if (*trans_id == NULL) { |
172 | status = U1DB_NOMEM; |
173 | + goto finish; |
174 | } |
175 | } |
176 | + status = U1DB_OK; |
177 | } |
178 | +finish: |
179 | sqlite3_finalize(statement); |
180 | return status; |
181 | } |
182 | @@ -1481,34 +1514,21 @@ |
183 | const char *trans_id) |
184 | { |
185 | int status = U1DB_OK; |
186 | - sqlite3_stmt *statement; |
187 | + char *known_trans_id = NULL; |
188 | |
189 | if (generation == 0) |
190 | return status; |
191 | - if (db == NULL) { |
192 | - return U1DB_INVALID_PARAMETER; |
193 | - } |
194 | - status = sqlite3_prepare_v2(db->sql_handle, |
195 | - "SELECT transaction_id FROM transaction_log WHERE generation = ?", -1, |
196 | - &statement, NULL); |
197 | - if (status != SQLITE_OK) { goto finish; } |
198 | - status = sqlite3_bind_int(statement, 1, generation); |
199 | - if (status != SQLITE_OK) { goto finish; } |
200 | - status = sqlite3_step(statement); |
201 | - if (status == SQLITE_DONE) { |
202 | - status = U1DB_INVALID_GENERATION; |
203 | + status = u1db__get_trans_id_for_gen(db, generation, &known_trans_id); |
204 | + if (status != U1DB_OK) |
205 | goto finish; |
206 | - } else if (status == SQLITE_ROW) { |
207 | - // Note: We may want to handle the column containing NULL |
208 | - if (strcmp(trans_id, |
209 | - (const char *)sqlite3_column_text(statement, 0)) == 0) { |
210 | + if (strcmp(trans_id, known_trans_id) == 0) { |
211 | status = U1DB_OK; |
212 | goto finish; |
213 | } |
214 | - status = U1DB_INVALID_TRANSACTION_ID; |
215 | - } |
216 | + status = U1DB_INVALID_TRANSACTION_ID; |
217 | finish: |
218 | - sqlite3_finalize(statement); |
219 | + if (known_trans_id != NULL) |
220 | + free(known_trans_id); |
221 | return status; |
222 | } |
223 | |
224 | |
225 | === modified file 'src/u1db_http_sync_target.c' |
226 | --- src/u1db_http_sync_target.c 2012-07-03 15:48:59 +0000 |
227 | +++ src/u1db_http_sync_target.c 2012-07-06 20:51:26 +0000 |
228 | @@ -41,9 +41,10 @@ |
229 | struct _http_request; |
230 | |
231 | static int st_http_get_sync_info(u1db_sync_target *st, |
232 | - const char *source_replica_uid, |
233 | - const char **st_replica_uid, int *st_gen, int *source_gen, |
234 | - char **trans_id); |
235 | + const char *source_replica_uid, |
236 | + const char **st_replica_uid, int *st_gen, |
237 | + char **st_trans_id, int *source_gen, |
238 | + char **trans_id); |
239 | |
240 | static int st_http_record_sync_info(u1db_sync_target *st, |
241 | const char *source_replica_uid, int source_gen, const char *trans_id); |
242 | @@ -366,15 +367,15 @@ |
243 | } |
244 | |
245 | static int |
246 | -st_http_get_sync_info(u1db_sync_target *st, |
247 | - const char *source_replica_uid, |
248 | - const char **st_replica_uid, int *st_gen, int *source_gen, |
249 | - char **trans_id) |
250 | +st_http_get_sync_info(u1db_sync_target *st, const char *source_replica_uid, |
251 | + const char **st_replica_uid, int *st_gen, |
252 | + char **st_trans_id, int *source_gen, char **trans_id) |
253 | { |
254 | struct _http_state *state; |
255 | struct _http_request req = {0}; |
256 | char *url = NULL; |
257 | const char *tmp = NULL; |
258 | + const char *tmp2 = NULL; |
259 | int status = U1DB_OK; |
260 | long http_code; |
261 | struct curl_slist *headers = NULL; |
262 | @@ -481,6 +482,21 @@ |
263 | goto finish; |
264 | } |
265 | *st_gen = json_object_get_int(obj); |
266 | + obj = json_object_object_get(json, "target_replica_transaction_id"); |
267 | + if (obj == NULL) { |
268 | + status = U1DB_INVALID_HTTP_RESPONSE; |
269 | + goto finish; |
270 | + } |
271 | + tmp2 = json_object_get_string(obj); |
272 | + if (tmp2 == NULL) { |
273 | + *st_trans_id = NULL; |
274 | + } else { |
275 | + *st_trans_id = strdup(tmp2); |
276 | + if (*st_trans_id == NULL) { |
277 | + status = U1DB_NOMEM; |
278 | + goto finish; |
279 | + } |
280 | + } |
281 | obj = json_object_object_get(json, "source_replica_generation"); |
282 | if (obj == NULL) { |
283 | status = U1DB_INVALID_HTTP_RESPONSE; |
284 | |
285 | === modified file 'src/u1db_sync_target.c' |
286 | --- src/u1db_sync_target.c 2012-07-03 15:48:59 +0000 |
287 | +++ src/u1db_sync_target.c 2012-07-06 20:51:26 +0000 |
288 | @@ -22,9 +22,10 @@ |
289 | |
290 | |
291 | static int st_get_sync_info(u1db_sync_target *st, |
292 | - const char *source_replica_uid, |
293 | - const char **st_replica_uid, int *st_gen, int *source_gen, |
294 | - char **source_trans_id); |
295 | + const char *source_replica_uid, |
296 | + const char **st_replica_uid, int *st_gen, |
297 | + char **st_trans_id, int *source_gen, |
298 | + char **source_trans_id); |
299 | |
300 | static int st_record_sync_info(u1db_sync_target *st, |
301 | const char *source_replica_uid, int source_gen, const char *trans_id); |
302 | @@ -105,8 +106,8 @@ |
303 | |
304 | static int |
305 | st_get_sync_info(u1db_sync_target *st, const char *source_replica_uid, |
306 | - const char **st_replica_uid, int *st_gen, int *source_gen, |
307 | - char **source_trans_id) |
308 | + const char **st_replica_uid, int *st_gen, char **st_trans_id, |
309 | + int *source_gen, char **source_trans_id) |
310 | { |
311 | int status = U1DB_OK; |
312 | u1database *db; |
313 | @@ -128,7 +129,7 @@ |
314 | status = u1db__get_replica_gen_and_trans_id( |
315 | db, source_replica_uid, source_gen, source_trans_id); |
316 | if (status != U1DB_OK) { goto finish; } |
317 | - status = u1db__get_generation(db, st_gen); |
318 | + status = u1db__get_generation_info(db, st_gen, st_trans_id); |
319 | finish: |
320 | return status; |
321 | } |
322 | @@ -594,6 +595,7 @@ |
323 | char *local_target_trans_id = NULL; |
324 | char *target_trans_id_known_by_local = NULL; |
325 | char *local_trans_id_known_by_target = NULL; |
326 | + char *target_trans_id = NULL; |
327 | int target_gen, local_gen; |
328 | int local_gen_known_by_target, target_gen_known_by_local; |
329 | |
330 | @@ -608,14 +610,15 @@ |
331 | if (status != U1DB_OK) { goto finish; } |
332 | // fprintf(stderr, "Local uid: %s\n", local_uid); |
333 | status = target->get_sync_info( |
334 | - target, local_uid, &target_uid, &target_gen, |
335 | + target, local_uid, &target_uid, &target_gen, &target_trans_id, |
336 | &local_gen_known_by_target, &local_trans_id_known_by_target); |
337 | if (status != U1DB_OK) { goto finish; } |
338 | status = u1db_validate_gen_and_trans_id( |
339 | db, local_gen_known_by_target, local_trans_id_known_by_target); |
340 | if (status != U1DB_OK) { goto finish; } |
341 | - status = u1db__get_replica_gen_and_trans_id(db, target_uid, |
342 | - &target_gen_known_by_local, &target_trans_id_known_by_local); |
343 | + status = u1db__get_replica_gen_and_trans_id( |
344 | + db, target_uid, &target_gen_known_by_local, |
345 | + &target_trans_id_known_by_local); |
346 | if (status != U1DB_OK) { goto finish; } |
347 | local_target_trans_id = target_trans_id_known_by_local; |
348 | local_gen = local_gen_known_by_target; |
349 | @@ -630,6 +633,9 @@ |
350 | if (local_gen == local_gen_known_by_target |
351 | && target_gen == target_gen_known_by_local) |
352 | { |
353 | + if (strcmp(target_trans_id, target_trans_id_known_by_local) != 0) { |
354 | + status = U1DB_INVALID_TRANSACTION_ID; |
355 | + } |
356 | // We know status == U1DB_OK, and we can shortcut the rest of the |
357 | // logic, no need to look for more information. |
358 | goto finish; |
359 | @@ -671,6 +677,9 @@ |
360 | if (local_trans_id_known_by_target != NULL) { |
361 | free(local_trans_id_known_by_target); |
362 | } |
363 | + if (target_trans_id != NULL) { |
364 | + free(target_trans_id); |
365 | + } |
366 | if (local_target_trans_id != NULL) { |
367 | if (target_trans_id_known_by_local == local_target_trans_id) { |
368 | // Don't double free |
369 | |
370 | === modified file 'u1db/__init__.py' |
371 | --- u1db/__init__.py 2012-07-03 15:48:59 +0000 |
372 | +++ u1db/__init__.py 2012-07-06 20:51:26 +0000 |
373 | @@ -290,7 +290,7 @@ |
374 | encountered during synchronization. If we've never synchronized |
375 | with the replica, this is (0, ''). |
376 | """ |
377 | - raise NotImplementedError(self._replica_gen_and_trans_id) |
378 | + raise NotImplementedError(self._get_replica_gen_and_trans_id) |
379 | |
380 | def _set_replica_gen_and_trans_id(self, other_replica_uid, |
381 | other_generation, other_transaction_id): |
382 | @@ -536,7 +536,7 @@ |
383 | :param source_replica_uid: Another replica which we might have |
384 | synchronized with in the past. |
385 | :return: (target_replica_uid, target_replica_generation, |
386 | - source_replica_last_known_generation, |
387 | + target_trans_id, source_replica_last_known_generation, |
388 | source_replica_last_known_transaction_id) |
389 | """ |
390 | raise NotImplementedError(self.get_sync_info) |
391 | |
392 | === modified file 'u1db/backends/__init__.py' |
393 | --- u1db/backends/__init__.py 2012-07-03 13:50:27 +0000 |
394 | +++ u1db/backends/__init__.py 2012-07-06 20:51:26 +0000 |
395 | @@ -102,6 +102,14 @@ |
396 | result.append(doc) |
397 | return result |
398 | |
399 | + def _get_trans_id_for_gen(self, generation): |
400 | + """Get the transaction id corresponding to a particular generation. |
401 | + |
402 | + Raises an InvalidGeneration when the generation does not exist. |
403 | + |
404 | + """ |
405 | + raise NotImplementedError(self._get_trans_id_for_gen) |
406 | + |
407 | def validate_gen_and_trans_id(self, generation, trans_id): |
408 | """Validate the generation and transaction id. |
409 | |
410 | @@ -109,10 +117,14 @@ |
411 | InvalidTransactionId when it does but with a different transaction id. |
412 | |
413 | """ |
414 | - raise NotImplementedError(self.validate_gen_and_trans_id) |
415 | + if generation == 0: |
416 | + return |
417 | + known_trans_id = self._get_trans_id_for_gen(generation) |
418 | + if known_trans_id != trans_id: |
419 | + raise errors.InvalidTransactionId |
420 | |
421 | def _validate_source(self, other_replica_uid, other_generation, |
422 | - other_transaction_id, cur_vcr, other_vcr): |
423 | + other_transaction_id): |
424 | """Validate the new generation and transaction id. |
425 | |
426 | other_generation must be greater than what we have stored for this |
427 | @@ -123,13 +135,11 @@ |
428 | old_transaction_id) = self._get_replica_gen_and_trans_id( |
429 | other_replica_uid) |
430 | if other_generation < old_generation: |
431 | - if cur_vcr.is_newer(other_vcr): |
432 | - return 'superseded' |
433 | raise errors.InvalidGeneration |
434 | if other_generation > old_generation: |
435 | - return 'ok' |
436 | + return |
437 | if other_transaction_id == old_transaction_id: |
438 | - return 'superseded' |
439 | + return |
440 | raise errors.InvalidTransactionId |
441 | |
442 | def _put_doc_if_newer(self, doc, save_conflict, replica_uid=None, |
443 | @@ -141,11 +151,8 @@ |
444 | else: |
445 | cur_vcr = VectorClockRev(cur_doc.rev) |
446 | if replica_uid is not None and replica_gen is not None: |
447 | - state = self._validate_source( |
448 | - replica_uid, replica_gen, replica_trans_id, cur_vcr, |
449 | - doc_vcr) |
450 | - if state != 'ok': |
451 | - return state, self._get_generation() |
452 | + self._validate_source( |
453 | + replica_uid, replica_gen, replica_trans_id) |
454 | if doc_vcr.is_newer(cur_vcr): |
455 | rev = doc.rev |
456 | self._prune_conflicts(doc, doc_vcr) |
457 | |
458 | === modified file 'u1db/backends/inmemory.py' |
459 | --- u1db/backends/inmemory.py 2012-07-03 15:48:59 +0000 |
460 | +++ u1db/backends/inmemory.py 2012-07-06 20:51:26 +0000 |
461 | @@ -85,16 +85,16 @@ |
462 | return len(self._transaction_log) |
463 | |
464 | def _get_generation_info(self): |
465 | + if not self._transaction_log: |
466 | + return 0, '' |
467 | return len(self._transaction_log), self._transaction_log[-1][1] |
468 | |
469 | - def validate_gen_and_trans_id(self, generation, trans_id): |
470 | + def _get_trans_id_for_gen(self, generation): |
471 | if generation == 0: |
472 | - return |
473 | + return '' |
474 | if generation > len(self._transaction_log): |
475 | raise errors.InvalidGeneration |
476 | - if self._transaction_log[generation - 1][1] == trans_id: |
477 | - return |
478 | - raise errors.InvalidTransactionId |
479 | + return self._transaction_log[generation - 1][1] |
480 | |
481 | def put_doc(self, doc): |
482 | if doc.doc_id is None: |
483 | @@ -448,10 +448,12 @@ |
484 | class InMemorySyncTarget(CommonSyncTarget): |
485 | |
486 | def get_sync_info(self, source_replica_uid): |
487 | - source_gen, trans_id = self._db._get_replica_gen_and_trans_id( |
488 | + source_gen, source_trans_id = self._db._get_replica_gen_and_trans_id( |
489 | source_replica_uid) |
490 | - return (self._db._replica_uid, len(self._db._transaction_log), |
491 | - source_gen, trans_id) |
492 | + my_gen, my_trans_id = self._db._get_generation_info() |
493 | + return ( |
494 | + self._db._replica_uid, my_gen, my_trans_id, source_gen, |
495 | + source_trans_id) |
496 | |
497 | def record_sync_info(self, source_replica_uid, source_replica_generation, |
498 | source_transaction_id): |
499 | |
500 | === modified file 'u1db/backends/sqlite_backend.py' |
501 | --- u1db/backends/sqlite_backend.py 2012-07-03 13:50:27 +0000 |
502 | +++ u1db/backends/sqlite_backend.py 2012-07-06 20:51:26 +0000 |
503 | @@ -271,12 +271,12 @@ |
504 | 'SELECT max(generation), transaction_id FROM transaction_log ') |
505 | val = c.fetchone() |
506 | if val[0] is None: |
507 | - return(0, val[1]) |
508 | + return(0, '') |
509 | return val |
510 | |
511 | - def validate_gen_and_trans_id(self, generation, trans_id): |
512 | + def _get_trans_id_for_gen(self, generation): |
513 | if generation == 0: |
514 | - return |
515 | + return '' |
516 | c = self._db_handle.cursor() |
517 | c.execute( |
518 | 'SELECT transaction_id FROM transaction_log WHERE generation = ?', |
519 | @@ -284,8 +284,7 @@ |
520 | val = c.fetchone() |
521 | if val is None: |
522 | raise errors.InvalidGeneration |
523 | - if val[0] != trans_id: |
524 | - raise errors.InvalidTransactionId |
525 | + return val[0] |
526 | |
527 | def _get_transaction_log(self): |
528 | c = self._db_handle.cursor() |
529 | @@ -788,10 +787,12 @@ |
530 | class SQLiteSyncTarget(CommonSyncTarget): |
531 | |
532 | def get_sync_info(self, source_replica_uid): |
533 | - source_gen, trans_id = self._db._get_replica_gen_and_trans_id( |
534 | + source_gen, source_trans_id = self._db._get_replica_gen_and_trans_id( |
535 | source_replica_uid) |
536 | - my_gen = self._db._get_generation() |
537 | - return self._db._replica_uid, my_gen, source_gen, trans_id |
538 | + my_gen, my_trans_id = self._db._get_generation_info() |
539 | + return ( |
540 | + self._db._replica_uid, my_gen, my_trans_id, source_gen, |
541 | + source_trans_id) |
542 | |
543 | def record_sync_info(self, source_replica_uid, source_replica_generation, |
544 | source_replica_transaction_id): |
545 | |
546 | === modified file 'u1db/remote/http_app.py' |
547 | --- u1db/remote/http_app.py 2012-07-03 15:48:59 +0000 |
548 | +++ u1db/remote/http_app.py 2012-07-06 20:51:26 +0000 |
549 | @@ -309,9 +309,10 @@ |
550 | result = self.target.get_sync_info(self.source_replica_uid) |
551 | self.responder.send_response_json( |
552 | target_replica_uid=result[0], target_replica_generation=result[1], |
553 | + target_replica_transaction_id=result[2], |
554 | source_replica_uid=self.source_replica_uid, |
555 | - source_replica_generation=result[2], |
556 | - source_transaction_id=result[3]) |
557 | + source_replica_generation=result[3], |
558 | + source_transaction_id=result[4]) |
559 | |
560 | @http_method(generation=int, |
561 | content_as_args=True, no_query=True) |
562 | |
563 | === modified file 'u1db/remote/http_target.py' |
564 | --- u1db/remote/http_target.py 2012-06-19 02:35:49 +0000 |
565 | +++ u1db/remote/http_target.py 2012-07-06 20:51:26 +0000 |
566 | @@ -42,6 +42,7 @@ |
567 | self._ensure_connection() |
568 | res, _ = self._request_json('GET', ['sync-from', source_replica_uid]) |
569 | return (res['target_replica_uid'], res['target_replica_generation'], |
570 | + res['target_replica_transaction_id'], |
571 | res['source_replica_generation'], res['source_transaction_id']) |
572 | |
573 | def record_sync_info(self, source_replica_uid, source_replica_generation, |
574 | |
575 | === modified file 'u1db/sync.py' |
576 | --- u1db/sync.py 2012-07-03 15:48:59 +0000 |
577 | +++ u1db/sync.py 2012-07-06 20:51:26 +0000 |
578 | @@ -18,6 +18,7 @@ |
579 | from itertools import izip |
580 | |
581 | import u1db |
582 | +from u1db import errors |
583 | |
584 | |
585 | class Synchronizer(object): |
586 | @@ -94,21 +95,21 @@ |
587 | sync_target = self.sync_target |
588 | # get target identifier, its current generation, |
589 | # and its last-seen database generation for this source |
590 | - (self.target_replica_uid, target_gen, target_my_gen, |
591 | + (self.target_replica_uid, target_gen, target_trans_id, target_my_gen, |
592 | target_my_trans_id) = sync_target.get_sync_info( |
593 | self.source._replica_uid) |
594 | - # validate that the generation and transaction id the target knows |
595 | - # about us are valid. |
596 | + # validate the generation and transaction id the target knows about us |
597 | self.source.validate_gen_and_trans_id( |
598 | target_my_gen, target_my_trans_id) |
599 | # what's changed since that generation and this current gen |
600 | my_gen, _, changes = self.source.whats_changed(target_my_gen) |
601 | |
602 | # this source last-seen database generation for the target |
603 | - (target_last_known_gen, |
604 | - target_trans_id) = self.source._get_replica_gen_and_trans_id( |
605 | - self.target_replica_uid) |
606 | + target_last_known_gen, target_last_known_trans_id = \ |
607 | + self.source._get_replica_gen_and_trans_id(self.target_replica_uid) |
608 | if not changes and target_last_known_gen == target_gen: |
609 | + if target_trans_id != target_last_known_trans_id: |
610 | + raise errors.InvalidTransactionId |
611 | return my_gen |
612 | changed_doc_ids = [doc_id for doc_id, _, _ in changes] |
613 | # prepare to send all the changed docs |
614 | @@ -123,7 +124,7 @@ |
615 | # the target, return target synced-up-to gen |
616 | new_gen, new_trans_id = sync_target.sync_exchange( |
617 | docs_by_generation, self.source._replica_uid, |
618 | - target_last_known_gen, target_trans_id, |
619 | + target_last_known_gen, target_last_known_trans_id, |
620 | self._insert_doc_from_target) |
621 | # record target synced-up-to generation including applying what we sent |
622 | self.source._set_replica_gen_and_trans_id( |
623 | |
624 | === modified file 'u1db/tests/c_backend_wrapper.pyx' |
625 | --- u1db/tests/c_backend_wrapper.pyx 2012-07-03 15:48:59 +0000 |
626 | +++ u1db/tests/c_backend_wrapper.pyx 2012-07-06 20:51:26 +0000 |
627 | @@ -81,9 +81,7 @@ |
628 | void *context, u1db_doc_callback cb) |
629 | int u1db_put_doc(u1database *db, u1db_document *doc) |
630 | int u1db__validate_source(u1database *db, const_char_ptr replica_uid, |
631 | - int replica_gen, const_char_ptr replica_trans_id, |
632 | - u1db_vectorclock *cur_vcr, |
633 | - u1db_vectorclock *other_vcr, int *state) |
634 | + int replica_gen, const_char_ptr replica_trans_id) |
635 | int u1db__put_doc_if_newer(u1database *db, u1db_document *doc, |
636 | int save_conflict, char *replica_uid, |
637 | int replica_gen, char *replica_trans_id, |
638 | @@ -175,10 +173,10 @@ |
639 | |
640 | ctypedef int (*u1db__trace_callback)(void *context, const_char_ptr state) |
641 | ctypedef struct u1db_sync_target: |
642 | - int (*get_sync_info)(u1db_sync_target *st, |
643 | - char *source_replica_uid, |
644 | - const_char_ptr *st_replica_uid, int *st_gen, int *source_gen, |
645 | - char **source_trans_id) nogil |
646 | + int (*get_sync_info)(u1db_sync_target *st, char *source_replica_uid, |
647 | + const_char_ptr *st_replica_uid, int *st_gen, |
648 | + char **st_trans_id, int *source_gen, |
649 | + char **source_trans_id) nogil |
650 | int (*record_sync_info)(u1db_sync_target *st, |
651 | char *source_replica_uid, int source_gen, char *trans_id) nogil |
652 | int (*sync_exchange)(u1db_sync_target *st, |
653 | @@ -206,6 +204,7 @@ |
654 | |
655 | int u1db__get_generation(u1database *, int *db_rev) |
656 | int u1db__get_generation_info(u1database *, int *db_rev, char **trans_id) |
657 | + int u1db__get_trans_id_for_gen(u1database *, int db_rev, char **trans_id) |
658 | int u1db_validate_gen_and_trans_id(u1database *, int db_rev, |
659 | const_char_ptr trans_id) |
660 | char *u1db__allocate_doc_id(u1database *) |
661 | @@ -698,18 +697,25 @@ |
662 | cdef const_char_ptr st_replica_uid = NULL |
663 | cdef int st_gen = 0, source_gen = 0, status |
664 | cdef char *trans_id = NULL |
665 | + cdef char *st_trans_id = NULL |
666 | |
667 | self._check() |
668 | assert self._st.get_sync_info != NULL, "get_sync_info is NULL?" |
669 | with nogil: |
670 | status = self._st.get_sync_info(self._st, source_replica_uid, |
671 | - &st_replica_uid, &st_gen, &source_gen, &trans_id) |
672 | + &st_replica_uid, &st_gen, &st_trans_id, &source_gen, &trans_id) |
673 | handle_status("get_sync_info", status) |
674 | res_trans_id = None |
675 | + res_st_trans_id = None |
676 | if trans_id != NULL: |
677 | res_trans_id = trans_id |
678 | free(trans_id) |
679 | - return (safe_str(st_replica_uid), st_gen, source_gen, res_trans_id) |
680 | + if st_trans_id != NULL: |
681 | + res_st_trans_id = st_trans_id |
682 | + free(st_trans_id) |
683 | + return ( |
684 | + safe_str(st_replica_uid), st_gen, res_st_trans_id, source_gen, |
685 | + res_trans_id) |
686 | |
687 | def record_sync_info(self, source_replica_uid, source_gen, source_trans_id): |
688 | cdef int status |
689 | @@ -941,26 +947,16 @@ |
690 | u1db_put_doc(self._db, doc._doc)) |
691 | return doc.rev |
692 | |
693 | - def _validate_source(self, replica_uid, replica_gen, replica_trans_id, |
694 | - cur_vcr, other_vcr): |
695 | + def _validate_source(self, replica_uid, replica_gen, replica_trans_id): |
696 | cdef const_char_ptr c_uid, c_trans_id |
697 | - cdef int c_gen, state = 0 |
698 | - cdef VectorClockRev cur |
699 | - cdef VectorClockRev other |
700 | + cdef int c_gen = 0 |
701 | |
702 | - cur = VectorClockRev(cur_vcr.as_str()) |
703 | - other = VectorClockRev(other_vcr.as_str()) |
704 | c_uid = replica_uid |
705 | c_trans_id = replica_trans_id |
706 | c_gen = replica_gen |
707 | handle_status( |
708 | "invalid generation or transaction id", |
709 | - u1db__validate_source( |
710 | - self._db, c_uid, c_gen, c_trans_id, cur._clock, other._clock, |
711 | - &state)) |
712 | - if state == U1DB_SUPERSEDED: |
713 | - return 'superseded' |
714 | - return 'ok' |
715 | + u1db__validate_source(self._db, c_uid, c_gen, c_trans_id)) |
716 | |
717 | def _put_doc_if_newer(self, CDocument doc, save_conflict, replica_uid=None, |
718 | replica_gen=None, replica_trans_id=None): |
719 | @@ -1100,6 +1096,18 @@ |
720 | "validate_gen_and_trans_id", |
721 | u1db_validate_gen_and_trans_id(self._db, generation, trans_id)) |
722 | |
723 | + def _get_trans_id_for_gen(self, generation): |
724 | + cdef char *trans_id = NULL |
725 | + |
726 | + handle_status( |
727 | + "_get_trans_id_for_gen", |
728 | + u1db__get_trans_id_for_gen(self._db, generation, &trans_id)) |
729 | + raw_trans_id = None |
730 | + if trans_id != NULL: |
731 | + raw_trans_id = trans_id |
732 | + free(trans_id) |
733 | + return raw_trans_id |
734 | + |
735 | def _get_replica_gen_and_trans_id(self, replica_uid): |
736 | cdef int generation, status |
737 | cdef char *trans_id = NULL |
738 | |
739 | === modified file 'u1db/tests/test_backends.py' |
740 | --- u1db/tests/test_backends.py 2012-07-04 15:43:00 +0000 |
741 | +++ u1db/tests/test_backends.py 2012-07-06 20:51:26 +0000 |
742 | @@ -402,11 +402,12 @@ |
743 | |
744 | def test_put_doc_if_newer_same_generation_same_txid(self): |
745 | self.db._set_replica_gen_and_trans_id('other', 1, 'T-sid') |
746 | - doc = self.make_document('doc_id', 'other:2', simple_doc) |
747 | + doc = self.db.create_doc(simple_doc) |
748 | + doc2 = self.make_document(doc.doc_id, 'other:1', simple_doc) |
749 | state, _ = self.db._put_doc_if_newer( |
750 | doc, save_conflict=False, replica_uid='other', replica_gen=1, |
751 | replica_trans_id='T-sid') |
752 | - self.assertEqual('superseded', state) |
753 | + self.assertEqual('converged', state) |
754 | |
755 | def test_put_doc_if_newer_wrong_transaction_id(self): |
756 | self.db._set_replica_gen_and_trans_id('other', 1, 'T-sid') |
757 | @@ -422,10 +423,10 @@ |
758 | doc_rev1 = doc.rev |
759 | doc.set_json(simple_doc) |
760 | self.db.put_doc(doc) |
761 | - self.db._set_replica_gen_and_trans_id('other', 5, 'T-sid') |
762 | + self.db._set_replica_gen_and_trans_id('other', 3, 'T-sid') |
763 | older_doc = self.make_document(doc.doc_id, doc_rev1, simple_doc) |
764 | state, _ = self.db._put_doc_if_newer( |
765 | - older_doc, save_conflict=False, replica_uid='other', replica_gen=3, |
766 | + older_doc, save_conflict=False, replica_uid='other', replica_gen=8, |
767 | replica_trans_id='T-irrelevant') |
768 | self.assertEqual('superseded', state) |
769 | |
770 | @@ -557,45 +558,17 @@ |
771 | |
772 | def test_validate_source_gen_and_trans_id_same(self): |
773 | self.db._set_replica_gen_and_trans_id('other', 1, 'T-sid') |
774 | - v1 = vectorclock.VectorClockRev('other:1|self:1') |
775 | - v2 = vectorclock.VectorClockRev('other:1|self:1') |
776 | - self.assertEqual( |
777 | - 'superseded', |
778 | - self.db._validate_source('other', 1, 'T-sid', v1, v2)) |
779 | + self.db._validate_source('other', 1, 'T-sid') |
780 | |
781 | def test_validate_source_gen_newer(self): |
782 | self.db._set_replica_gen_and_trans_id('other', 1, 'T-sid') |
783 | - v1 = vectorclock.VectorClockRev('other:1|self:1') |
784 | - v2 = vectorclock.VectorClockRev('other:2|self:2') |
785 | - self.assertEqual( |
786 | - 'ok', |
787 | - self.db._validate_source('other', 2, 'T-whatevs', v1, v2)) |
788 | + self.db._validate_source('other', 2, 'T-whatevs') |
789 | |
790 | def test_validate_source_wrong_txid(self): |
791 | self.db._set_replica_gen_and_trans_id('other', 1, 'T-sid') |
792 | - v1 = vectorclock.VectorClockRev('other:1|self:1') |
793 | - v2 = vectorclock.VectorClockRev('other:2|self:2') |
794 | self.assertRaises( |
795 | errors.InvalidTransactionId, |
796 | - self.db._validate_source, 'other', 1, 'T-sad', v1, v2) |
797 | - |
798 | - def test_validate_source_gen_older_and_vcr_older(self): |
799 | - self.db._set_replica_gen_and_trans_id('other', 1, 'T-sid') |
800 | - self.db._set_replica_gen_and_trans_id('other', 2, 'T-sod') |
801 | - v1 = vectorclock.VectorClockRev('other:1|self:1') |
802 | - v2 = vectorclock.VectorClockRev('other:2|self:2') |
803 | - self.assertEqual( |
804 | - 'superseded', |
805 | - self.db._validate_source('other', 1, 'T-sid', v2, v1)) |
806 | - |
807 | - def test_validate_source_gen_older_vcr_newer(self): |
808 | - self.db._set_replica_gen_and_trans_id('other', 1, 'T-sid') |
809 | - self.db._set_replica_gen_and_trans_id('other', 2, 'T-sod') |
810 | - v1 = vectorclock.VectorClockRev('other:1|self:1') |
811 | - v2 = vectorclock.VectorClockRev('other:2|self:2') |
812 | - self.assertRaises( |
813 | - errors.InvalidGeneration, |
814 | - self.db._validate_source, 'other', 1, 'T-sid', v1, v2) |
815 | + self.db._validate_source, 'other', 1, 'T-sad') |
816 | |
817 | |
818 | class LocalDatabaseWithConflictsTests(tests.DatabaseBaseTests): |
819 | |
820 | === modified file 'u1db/tests/test_c_backend.py' |
821 | --- u1db/tests/test_c_backend.py 2012-07-03 15:48:59 +0000 |
822 | +++ u1db/tests/test_c_backend.py 2012-07-06 20:51:26 +0000 |
823 | @@ -75,7 +75,7 @@ |
824 | |
825 | def test__get_generation_info(self): |
826 | db = c_backend_wrapper.CDatabase(':memory:') |
827 | - self.assertEqual((0, None), db._get_generation_info()) |
828 | + self.assertEqual((0, ''), db._get_generation_info()) |
829 | db.create_doc(tests.simple_doc) |
830 | info = db._get_generation_info() |
831 | self.assertEqual(1, info[0]) |
832 | |
833 | === modified file 'u1db/tests/test_http_app.py' |
834 | --- u1db/tests/test_http_app.py 2012-07-03 15:48:59 +0000 |
835 | +++ u1db/tests/test_http_app.py 2012-07-06 20:51:26 +0000 |
836 | @@ -697,6 +697,7 @@ |
837 | self.assertEqual('application/json', resp.header('content-type')) |
838 | self.assertEqual(dict(target_replica_uid='db0', |
839 | target_replica_generation=0, |
840 | + target_replica_transaction_id='', |
841 | source_replica_uid='other-id', |
842 | source_replica_generation=1, |
843 | source_transaction_id='T-transid'), |
844 | @@ -722,7 +723,8 @@ |
845 | } |
846 | |
847 | gens = [] |
848 | - _do_set_replica_gen_and_trans_id = self.db0._do_set_replica_gen_and_trans_id |
849 | + _do_set_replica_gen_and_trans_id = \ |
850 | + self.db0._do_set_replica_gen_and_trans_id |
851 | |
852 | def set_sync_generation_witness(other_uid, other_gen, other_trans_id): |
853 | gens.append((other_uid, other_gen)) |
854 | |
855 | === modified file 'u1db/tests/test_remote_sync_target.py' |
856 | --- u1db/tests/test_remote_sync_target.py 2012-07-03 15:48:59 +0000 |
857 | +++ u1db/tests/test_remote_sync_target.py 2012-07-06 20:51:26 +0000 |
858 | @@ -173,7 +173,7 @@ |
859 | db = self.request_state._create_database('test') |
860 | db._set_replica_gen_and_trans_id('other-id', 1, 'T-transid') |
861 | remote_target = self.getSyncTarget('test') |
862 | - self.assertEqual(('test', 0, 1, 'T-transid'), |
863 | + self.assertEqual(('test', 0, '', 1, 'T-transid'), |
864 | remote_target.get_sync_info('other-id')) |
865 | |
866 | def test_record_sync_info(self): |
867 | |
868 | === modified file 'u1db/tests/test_sqlite_backend.py' |
869 | --- u1db/tests/test_sqlite_backend.py 2012-06-15 19:41:07 +0000 |
870 | +++ u1db/tests/test_sqlite_backend.py 2012-07-06 20:51:26 +0000 |
871 | @@ -153,7 +153,7 @@ |
872 | self.assertEqual(0, self.db._get_generation()) |
873 | |
874 | def test__get_generation_info(self): |
875 | - self.assertEqual((0, None), self.db._get_generation_info()) |
876 | + self.assertEqual((0, ''), self.db._get_generation_info()) |
877 | |
878 | def test_create_index(self): |
879 | self.db.create_index('test-idx', "key") |
880 | |
881 | === modified file 'u1db/tests/test_sync.py' |
882 | --- u1db/tests/test_sync.py 2012-07-04 16:03:39 +0000 |
883 | +++ u1db/tests/test_sync.py 2012-07-06 20:51:26 +0000 |
884 | @@ -143,18 +143,19 @@ |
885 | self.assertIsNot(None, self.st) |
886 | |
887 | def test_get_sync_info(self): |
888 | - self.assertEqual(('test', 0, 0, ''), self.st.get_sync_info('other')) |
889 | + self.assertEqual( |
890 | + ('test', 0, '', 0, ''), self.st.get_sync_info('other')) |
891 | |
892 | def test_create_doc_updates_sync_info(self): |
893 | - self.assertEqual(('test', 0, 0, ''), self.st.get_sync_info('other')) |
894 | + self.assertEqual( |
895 | + ('test', 0, '', 0, ''), self.st.get_sync_info('other')) |
896 | self.db.create_doc(simple_doc) |
897 | - self.assertEqual(('test', 1, 0, ''), self.st.get_sync_info('other')) |
898 | + self.assertEqual(1, self.st.get_sync_info('other')[1]) |
899 | |
900 | def test_record_sync_info(self): |
901 | - self.assertEqual(('test', 0, 0, ''), self.st.get_sync_info('replica')) |
902 | self.st.record_sync_info('replica', 10, 'T-transid') |
903 | - self.assertEqual(('test', 0, 10, 'T-transid'), |
904 | - self.st.get_sync_info('replica')) |
905 | + self.assertEqual( |
906 | + ('test', 0, '', 10, 'T-transid'), self.st.get_sync_info('replica')) |
907 | |
908 | def test_sync_exchange(self): |
909 | docs_by_gen = [ |
910 | @@ -168,7 +169,7 @@ |
911 | last_trans_id = self.getLastTransId(self.db) |
912 | self.assertEqual(([], 1, last_trans_id), |
913 | (self.other_changes, new_gen, last_trans_id)) |
914 | - self.assertEqual(10, self.st.get_sync_info('replica')[2]) |
915 | + self.assertEqual(10, self.st.get_sync_info('replica')[3]) |
916 | |
917 | def test_sync_exchange_deleted(self): |
918 | doc = self.db.create_doc('{}') |
919 | @@ -184,7 +185,7 @@ |
920 | last_trans_id = self.getLastTransId(self.db) |
921 | self.assertEqual(([], 2, last_trans_id), |
922 | (self.other_changes, new_gen, trans_id)) |
923 | - self.assertEqual(10, self.st.get_sync_info('replica')[2]) |
924 | + self.assertEqual(10, self.st.get_sync_info('replica')[3]) |
925 | |
926 | def test_sync_exchange_push_many(self): |
927 | docs_by_gen = [ |
928 | @@ -200,7 +201,7 @@ |
929 | last_trans_id = self.getLastTransId(self.db) |
930 | self.assertEqual(([], 2, last_trans_id), |
931 | (self.other_changes, new_gen, trans_id)) |
932 | - self.assertEqual(11, self.st.get_sync_info('replica')[2]) |
933 | + self.assertEqual(11, self.st.get_sync_info('replica')[3]) |
934 | |
935 | def test_sync_exchange_refuses_conflicts(self): |
936 | doc = self.db.create_doc(simple_doc) |
937 | @@ -378,7 +379,7 @@ |
938 | last_trans_id = self.getLastTransId(self.db) |
939 | self.assertEqual(([], 1, last_trans_id), |
940 | (self.other_changes, new_gen, trans_id)) |
941 | - self.assertEqual(10, self.st.get_sync_info(db2._replica_uid)[2]) |
942 | + self.assertEqual(10, self.st.get_sync_info(db2._replica_uid)[3]) |
943 | |
944 | def test__set_trace_hook(self): |
945 | called = [] |
946 | @@ -424,7 +425,8 @@ |
947 | return db |
948 | |
949 | |
950 | -def sync_via_synchronizer_and_http(test, db_source, db_target, trace_hook=None): |
951 | +def sync_via_synchronizer_and_http(test, db_source, db_target, |
952 | + trace_hook=None): |
953 | if trace_hook: |
954 | test.skipTest("trace_hook unsupported over http") |
955 | path = test._http_at[db_target] |
956 | @@ -988,6 +990,24 @@ |
957 | self.assertRaises( |
958 | errors.InvalidTransactionId, self.sync, self.db1, db3) |
959 | |
960 | + def test_sync_detects_rollback_and_divergence_in_source(self): |
961 | + self.db1.create_doc(tests.simple_doc, doc_id="divergent") |
962 | + self.sync(self.db1, self.db2) |
963 | + self.db1.create_doc(tests.simple_doc) |
964 | + self.db2._set_replica_gen_and_trans_id( |
965 | + self.db1._replica_uid, 2, 'T-madeup') |
966 | + self.assertRaises( |
967 | + errors.InvalidTransactionId, self.sync, self.db1, self.db2) |
968 | + |
969 | + def test_sync_detects_rollback_and_divergence_in_target(self): |
970 | + self.db1.create_doc(tests.simple_doc, doc_id="divergent") |
971 | + self.sync(self.db1, self.db2) |
972 | + self.db2.create_doc(tests.simple_doc) |
973 | + self.db1._set_replica_gen_and_trans_id( |
974 | + self.db2._replica_uid, 2, 'T-madeup') |
975 | + self.assertRaises( |
976 | + errors.InvalidTransactionId, self.sync, self.db1, self.db2) |
977 | + |
978 | |
979 | class TestDbSync(tests.TestCaseWithServer): |
980 | """Test db.sync remote sync shortcut""" |
981 | @@ -1032,7 +1052,8 @@ |
982 | self.assertEqual(2, len(self.db2._get_transaction_log())) |
983 | progress1 = [] |
984 | progress2 = [] |
985 | - _do_set_replica_gen_and_trans_id = self.db1._do_set_replica_gen_and_trans_id |
986 | + _do_set_replica_gen_and_trans_id = \ |
987 | + self.db1._do_set_replica_gen_and_trans_id |
988 | |
989 | def set_sync_generation_witness1(other_uid, other_gen, trans_id): |
990 | progress1.append((other_uid, other_gen, |
991 | @@ -1040,8 +1061,8 @@ |
992 | _do_set_replica_gen_and_trans_id(other_uid, other_gen, trans_id) |
993 | self.patch(self.db1, '_do_set_replica_gen_and_trans_id', |
994 | set_sync_generation_witness1) |
995 | - |
996 | - _do_set_replica_gen_and_trans_id2 = self.db2._do_set_replica_gen_and_trans_id |
997 | + _do_set_replica_gen_and_trans_id2 = \ |
998 | + self.db2._do_set_replica_gen_and_trans_id |
999 | |
1000 | def set_sync_generation_witness2(other_uid, other_gen, trans_id): |
1001 | progress2.append((other_uid, other_gen, |