Merge lp:~jameinel/u1db/whats_changed_999574 into lp:u1db

Proposed by John A Meinel
Status: Superseded
Proposed branch: lp:~jameinel/u1db/whats_changed_999574
Merge into: lp:u1db
Diff against target: 1344 lines (+305/-210)
21 files modified
include/u1db/u1db.h (+8/-7)
include/u1db/u1db_internal.h (+13/-6)
src/u1db.c (+28/-15)
src/u1db_http_sync_target.c (+19/-6)
src/u1db_sync_target.c (+26/-13)
u1db/__init__.py (+26/-15)
u1db/backends/__init__.py (+1/-1)
u1db/backends/dbschema.sql (+5/-5)
u1db/backends/inmemory.py (+20/-15)
u1db/backends/sqlite_backend.py (+23/-15)
u1db/remote/http_app.py (+5/-3)
u1db/remote/http_target.py (+5/-3)
u1db/sync.py (+11/-11)
u1db/tests/__init__.py (+4/-0)
u1db/tests/c_backend_wrapper.pyx (+34/-32)
u1db/tests/test_backends.py (+27/-16)
u1db/tests/test_c_backend.py (+2/-1)
u1db/tests/test_http_app.py (+11/-10)
u1db/tests/test_https.py (+2/-2)
u1db/tests/test_remote_sync_target.py (+6/-6)
u1db/tests/test_sync.py (+29/-28)
To merge this branch: bzr merge lp:~jameinel/u1db/whats_changed_999574
Reviewer Review Type Date Requested Status
Ubuntu One hackers Pending
Review via email: mp+106339@code.launchpad.net

This proposal has been superseded by a proposal from 2012-05-18.

Description of the change

This is hopefully a small patch that is easier to review.

It just updates the whats_changed api so that it returns the transaction_id along with the database generation. Hopefully I updated enough of the docs to match it.

It does mean that we have to expose the transaction ids a bit in the test suite, for now I just went with '_get_transaction_log()' and pull out the bit we care about. I don't really want to add another not-for-use-in-production-code api for grabbing it. (You could argue that put_doc could return it.)

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.h'
2--- include/u1db/u1db.h 2012-05-17 15:13:42 +0000
3+++ include/u1db/u1db.h 2012-05-18 09:33:18 +0000
4@@ -198,15 +198,16 @@
5 * get all changes in the database. The integer will be updated
6 * to point at the current generation.
7 * @param cb A callback function. This will be called passing in 'context',
8- * and a document identifier for each document that has been modified.
9- * The doc_id string is transient, so callers must copy it to
10- * their own memory if they want to keep it.
11- * If a document is changed more than once, it is currently
12- * undefined whether this will call cb() once per change, or just
13- * once per doc_id.
14+ * and a document identifier for each document that has been
15+ * modified. This includes the generation and associated
16+ * transaction id for each change. If a document is modified more
17+ * than once, only the most recent change will be given.
18+ * Note that the strings passed are transient, so must be copied
19+ * if callers want to use them after they return.
20 * @param context Opaque context, passed back to the caller.
21 */
22-int u1db_whats_changed(u1database *db, int *gen, void *context, u1db_doc_id_gen_callback cb);
23+int u1db_whats_changed(u1database *db, int *gen, void *context,
24+ u1db_trans_info_callback cb);
25
26
27 /**
28
29=== modified file 'include/u1db/u1db_internal.h'
30--- include/u1db/u1db_internal.h 2012-05-17 15:13:42 +0000
31+++ include/u1db/u1db_internal.h 2012-05-18 09:33:18 +0000
32@@ -65,10 +65,14 @@
33 * target, matches st_replica_uid
34 * @param source_gen (OUT) The last generation of source_replica_uid
35 * that st has synchronized with.
36+ * @param trans_id (OUT) The transaction id associated with the
37+ * source generation, the memory must be freed by
38+ * the caller.
39 */
40 int (*get_sync_info)(u1db_sync_target *st,
41 const char *source_replica_uid,
42- const char **st_replica_uid, int *st_gen, int *source_gen);
43+ const char **st_replica_uid, int *st_gen, int *source_gen,
44+ char **trans_id);
45 /**
46 * Set the synchronization information about another replica.
47 *
48@@ -78,9 +82,10 @@
49 * want to synchronize from.
50 * @param source_gen The last generation of source_replica_uid
51 * that st has synchronized with.
52+ * @param trans_id The transaction id associated with source_gen
53 */
54 int (*record_sync_info)(u1db_sync_target *st,
55- const char *source_replica_uid, int source_gen);
56+ const char *source_replica_uid, int source_gen, const char *trans_id);
57
58 /**
59 * Send documents to the target, and receive the response.
60@@ -249,16 +254,18 @@
61 * @param replica_uid The identifier for the other database
62 * @param generation (OUT) The last generation that we know we synchronized
63 * with the other database.
64+ * @param trans_id (OUT) The transaction id associated with the generation.
65+ * Callers must free the data.
66 */
67-int u1db__get_sync_generation(u1database *db, const char *replica_uid,
68- int *generation);
69+int u1db__get_sync_gen_info(u1database *db, const char *replica_uid,
70+ int *generation, char **trans_id);
71
72 /**
73 * Set the known sync generation for another replica.
74 *
75 */
76-int u1db__set_sync_generation(u1database *db, const char *replica_uid,
77- int generation);
78+int u1db__set_sync_info(u1database *db, const char *replica_uid,
79+ int generation, const char *trans_id);
80
81 /**
82 * Internal sync api, get the stored information about another machine.
83
84=== modified file 'src/u1db.c'
85--- src/u1db.c 2012-05-17 15:26:01 +0000
86+++ src/u1db.c 2012-05-18 09:33:18 +0000
87@@ -810,7 +810,7 @@
88 (stored_doc_rev != NULL));
89 }
90 if (status == U1DB_OK && replica_uid != NULL) {
91- status = u1db__set_sync_generation(db, replica_uid, replica_gen);
92+ status = u1db__set_sync_info(db, replica_uid, replica_gen, "T-sid");
93 }
94 if (status == U1DB_OK && at_gen != NULL) {
95 status = u1db__get_generation(db, at_gen);
96@@ -1108,7 +1108,7 @@
97
98 int
99 u1db_whats_changed(u1database *db, int *gen, void *context,
100- int (*cb)(void *, const char *doc_id, int gen))
101+ u1db_trans_info_callback cb)
102 {
103 int status;
104 sqlite3_stmt *statement;
105@@ -1116,8 +1116,8 @@
106 return -1; // Bad parameters
107 }
108 status = sqlite3_prepare_v2(db->sql_handle,
109- "SELECT max(generation) as g, doc_id FROM transaction_log"
110- " WHERE generation > ?"
111+ "SELECT max(generation) as g, doc_id, transaction_id"
112+ " FROM transaction_log WHERE generation > ?"
113 " GROUP BY doc_id ORDER BY g",
114 -1, &statement, NULL);
115 if (status != SQLITE_OK) {
116@@ -1131,13 +1131,15 @@
117 status = sqlite3_step(statement);
118 while (status == SQLITE_ROW) {
119 int local_gen;
120- char *doc_id;
121+ const char *doc_id;
122+ const char *trans_id;
123 local_gen = sqlite3_column_int(statement, 0);
124 if (local_gen > *gen) {
125 *gen = local_gen;
126 }
127- doc_id = (char *)sqlite3_column_text(statement, 1);
128- cb(context, doc_id, local_gen);
129+ doc_id = (const char *)sqlite3_column_text(statement, 1);
130+ trans_id = (const char *)sqlite3_column_text(statement, 2);
131+ cb(context, doc_id, local_gen, trans_id);
132 status = sqlite3_step(statement);
133 }
134 if (status == SQLITE_DONE) {
135@@ -1315,18 +1317,22 @@
136
137
138 int
139-u1db__get_sync_generation(u1database *db, const char *replica_uid,
140- int *generation)
141+u1db__get_sync_gen_info(u1database *db, const char *replica_uid,
142+ int *generation, char **trans_id)
143 {
144 int status;
145 sqlite3_stmt *statement;
146+ const char *tmp;
147
148- if (db == NULL || replica_uid == NULL || generation == NULL) {
149+ if (db == NULL || replica_uid == NULL || generation == NULL
150+ || trans_id == NULL)
151+ {
152 return U1DB_INVALID_PARAMETER;
153 }
154 status = sqlite3_prepare_v2(db->sql_handle,
155- "SELECT known_generation FROM sync_log WHERE replica_uid = ?", -1,
156- &statement, NULL);
157+ "SELECT known_generation, known_transaction_id"
158+ " FROM sync_log WHERE replica_uid = ?",
159+ -1, &statement, NULL);
160 if (status != SQLITE_OK) { goto finish; }
161 status = sqlite3_bind_text(statement, 1, replica_uid, -1, SQLITE_TRANSIENT);
162 if (status != SQLITE_OK) { goto finish; }
163@@ -1334,10 +1340,17 @@
164 if (status == SQLITE_DONE) {
165 status = SQLITE_OK;
166 *generation = 0;
167+ *trans_id = strdup("");
168 } else if (status == SQLITE_ROW) {
169 *generation = sqlite3_column_int(statement, 0);
170+ // Note: We may want to handle the column containing NULL
171+ tmp = (const char *)sqlite3_column_text(statement, 1);
172+ *trans_id = strdup(tmp);
173 status = SQLITE_OK;
174 }
175+ if (*trans_id == NULL) {
176+ status = U1DB_NOMEM;
177+ }
178 finish:
179 sqlite3_finalize(statement);
180 return status;
181@@ -1345,8 +1358,8 @@
182
183
184 int
185-u1db__set_sync_generation(u1database *db, const char *replica_uid,
186- int generation)
187+u1db__set_sync_info(u1database *db, const char *replica_uid,
188+ int generation, const char *trans_id)
189 {
190 int status;
191 sqlite3_stmt *statement;
192@@ -1364,7 +1377,7 @@
193 if (status != SQLITE_OK) { goto finish; }
194 status = sqlite3_bind_int(statement, 2, generation);
195 if (status != SQLITE_OK) { goto finish; }
196- status = sqlite3_bind_text(statement, 3, "", -1, SQLITE_TRANSIENT);
197+ status = sqlite3_bind_text(statement, 3, trans_id, -1, SQLITE_TRANSIENT);
198 if (status != SQLITE_OK) { goto finish; }
199 status = sqlite3_step(statement);
200 if (status == SQLITE_DONE) {
201
202=== modified file 'src/u1db_http_sync_target.c'
203--- src/u1db_http_sync_target.c 2012-05-09 16:09:04 +0000
204+++ src/u1db_http_sync_target.c 2012-05-18 09:33:18 +0000
205@@ -34,17 +34,18 @@
206
207 static int st_http_get_sync_info(u1db_sync_target *st,
208 const char *source_replica_uid,
209- const char **st_replica_uid, int *st_gen, int *source_gen);
210+ const char **st_replica_uid, int *st_gen, int *source_gen,
211+ char **trans_id);
212
213 static int st_http_record_sync_info(u1db_sync_target *st,
214- const char *source_replica_uid, int source_gen);
215+ const char *source_replica_uid, int source_gen, const char *trans_id);
216
217 static int st_http_get_sync_exchange(u1db_sync_target *st,
218 const char *source_replica_uid,
219 int source_gen,
220 u1db_sync_exchange **exchange);
221 static int st_http_sync_exchange(u1db_sync_target *st,
222- const char *source_replica_uid,
223+ const char *source_replica_uid,
224 int n_docs, u1db_document **docs,
225 int *generations, int *target_gen, void *context,
226 u1db_doc_gen_callback cb);
227@@ -355,7 +356,8 @@
228 static int
229 st_http_get_sync_info(u1db_sync_target *st,
230 const char *source_replica_uid,
231- const char **st_replica_uid, int *st_gen, int *source_gen)
232+ const char **st_replica_uid, int *st_gen, int *source_gen,
233+ char **trans_id)
234 {
235 struct _http_state *state;
236 struct _http_request req = {0};
237@@ -451,6 +453,15 @@
238 goto finish;
239 }
240 *source_gen = json_object_get_int(obj);
241+ obj = json_object_object_get(json, "source_transaction_id");
242+ if (obj == NULL) {
243+ status = U1DB_INVALID_HTTP_RESPONSE;
244+ goto finish;
245+ }
246+ *trans_id = strdup(json_object_get_string(obj));
247+ if (*trans_id == NULL) {
248+ status = U1DB_NOMEM;
249+ }
250 finish:
251 if (req.header_buffer != NULL) {
252 free(req.header_buffer);
253@@ -508,7 +519,7 @@
254
255 static int
256 st_http_record_sync_info(u1db_sync_target *st,
257- const char *source_replica_uid, int source_gen)
258+ const char *source_replica_uid, int source_gen, const char *trans_id)
259 {
260 struct _http_state *state;
261 struct _http_request req = {0};
262@@ -537,6 +548,8 @@
263 goto finish;
264 }
265 json_object_object_add(json, "generation", json_object_new_int(source_gen));
266+ json_object_object_add(json, "transaction_id",
267+ json_object_new_string(trans_id));
268 raw_body = json_object_to_json_string(json);
269 raw_len = strlen(raw_body);
270 req.state = state;
271@@ -561,7 +574,7 @@
272 status = curl_easy_setopt(state->curl, CURLOPT_INFILESIZE_LARGE,
273 (curl_off_t)req.num_put_bytes);
274 if (status != CURLE_OK) { goto finish; }
275- status = maybe_sign_url(st, "PUT", url, &headers);
276+ status = maybe_sign_url(st, "PUT", url, &headers);
277 if (status != U1DB_OK) { goto finish; }
278
279 // Now actually send the data
280
281=== modified file 'src/u1db_sync_target.c'
282--- src/u1db_sync_target.c 2012-05-17 13:25:42 +0000
283+++ src/u1db_sync_target.c 2012-05-18 09:33:18 +0000
284@@ -23,10 +23,11 @@
285
286 static int st_get_sync_info(u1db_sync_target *st,
287 const char *source_replica_uid,
288- const char **st_replica_uid, int *st_gen, int *source_gen);
289+ const char **st_replica_uid, int *st_gen, int *source_gen,
290+ char **trans_id);
291
292 static int st_record_sync_info(u1db_sync_target *st,
293- const char *source_replica_uid, int source_gen);
294+ const char *source_replica_uid, int source_gen, const char *trans_id);
295
296 static int st_sync_exchange(u1db_sync_target *st,
297 const char *source_replica_uid, int n_docs,
298@@ -100,7 +101,8 @@
299
300 static int
301 st_get_sync_info(u1db_sync_target *st, const char *source_replica_uid,
302- const char **st_replica_uid, int *st_gen, int *source_gen)
303+ const char **st_replica_uid, int *st_gen, int *source_gen,
304+ char **trans_id)
305 {
306 int status = U1DB_OK;
307 u1database *db;
308@@ -119,7 +121,8 @@
309 db = (u1database *)st->implementation;
310 status = u1db_get_replica_uid(db, st_replica_uid);
311 if (status != U1DB_OK) { goto finish; }
312- status = u1db__get_sync_generation(db, source_replica_uid, source_gen);
313+ status = u1db__get_sync_gen_info(db, source_replica_uid, source_gen,
314+ trans_id);
315 if (status != U1DB_OK) { goto finish; }
316 status = u1db__get_generation(db, st_gen);
317 finish:
318@@ -129,7 +132,7 @@
319
320 static int
321 st_record_sync_info(u1db_sync_target *st, const char *source_replica_uid,
322- int source_gen)
323+ int source_gen, const char *trans_id)
324 {
325 int status;
326 u1database *db;
327@@ -141,7 +144,7 @@
328 if (status != U1DB_OK) { goto finish; }
329 }
330 db = (u1database *)st->implementation;
331- status = u1db__set_sync_generation(db, source_replica_uid, source_gen);
332+ status = u1db__set_sync_info(db, source_replica_uid, source_gen, trans_id);
333 finish:
334 return status;
335 }
336@@ -301,7 +304,8 @@
337 // Callback for whats_changed to map the callback into the sync_exchange
338 // doc_ids_to_return array.
339 static int
340-whats_changed_to_doc_ids(void *context, const char *doc_id, int gen)
341+whats_changed_to_doc_ids(void *context, const char *doc_id, int gen,
342+ const char *trans_id)
343 {
344 struct lh_entry *e;
345 struct _whats_changed_doc_ids_state *state;
346@@ -543,6 +547,8 @@
347 struct _whats_changed_doc_ids_state to_send_state = {0};
348 struct _return_doc_state return_doc_state = {0};
349 const char *target_uid, *local_uid;
350+ char *target_trans_id_known_by_local = NULL;
351+ char *local_trans_id_known_by_target = NULL;
352 int target_gen, local_gen;
353 int local_gen_known_by_target, target_gen_known_by_local;
354
355@@ -556,10 +562,10 @@
356 if (status != U1DB_OK) { goto finish; }
357 // fprintf(stderr, "Local uid: %s\n", local_uid);
358 status = target->get_sync_info(target, local_uid, &target_uid, &target_gen,
359- &local_gen_known_by_target);
360+ &local_gen_known_by_target, &local_trans_id_known_by_target);
361 if (status != U1DB_OK) { goto finish; }
362- status = u1db__get_sync_generation(db, target_uid,
363- &target_gen_known_by_local);
364+ status = u1db__get_sync_gen_info(db, target_uid,
365+ &target_gen_known_by_local, &target_trans_id_known_by_local);
366 if (status != U1DB_OK) { goto finish; }
367 local_gen = local_gen_known_by_target;
368
369@@ -589,17 +595,24 @@
370 if (status != U1DB_OK) { goto finish; }
371 // Now we successfully sent and received docs, make sure we record the
372 // current remote generation
373- status = u1db__set_sync_generation(db, target_uid,
374- target_gen_known_by_local);
375+ status = u1db__set_sync_info(db, target_uid, target_gen_known_by_local,
376+ "T-sid");
377 if (status != U1DB_OK) { goto finish; }
378 if (return_doc_state.num_inserted > 0 &&
379 ((*local_gen_before_sync + return_doc_state.num_inserted)
380 == local_gen))
381 {
382- status = target->record_sync_info(target, local_uid, local_gen);
383+ status = target->record_sync_info(target, local_uid, local_gen,
384+ "T-sid");
385 if (status != U1DB_OK) { goto finish; }
386 }
387 finish:
388+ if (local_trans_id_known_by_target != NULL) {
389+ free(local_trans_id_known_by_target);
390+ }
391+ if (target_trans_id_known_by_local != NULL) {
392+ free(target_trans_id_known_by_local);
393+ }
394 if (to_send_state.doc_ids_to_return != NULL) {
395 int i;
396
397
398=== modified file 'u1db/__init__.py'
399--- u1db/__init__.py 2012-05-17 13:22:20 +0000
400+++ u1db/__init__.py 2012-05-18 09:33:18 +0000
401@@ -57,11 +57,12 @@
402
403 :param old_generation: The generation of the database in the old
404 state.
405- :return: (cur_generation, [(doc_id, generation),...])
406+ :return: (cur_generation, [(doc_id, generation, trans_id),...])
407 The current generation of the database, and a list of of
408 changed documents since old_generation, represented by tuples
409- with for each document its doc_id and the generation corresponding
410- to the last intervening change and sorted by generation
411+ with for each document its doc_id and the generation and
412+ transaction id corresponding to the last intervening change and
413+ sorted by generation (old changes first)
414 """
415 raise NotImplementedError(self.whats_changed)
416
417@@ -216,29 +217,35 @@
418 from u1db.remote.http_target import HTTPSyncTarget
419 return Synchronizer(self, HTTPSyncTarget(url)).sync()
420
421- def _get_sync_generation(self, other_replica_uid):
422- """Return the last known database generation of the other db replica.
423+ def _get_sync_gen_info(self, other_replica_uid):
424+ """Return the last known information about the other db replica.
425
426 When you do a synchronization with another replica, the Database keeps
427- track of what generation the other database replica was at.
428- This way we only have to request data that is newer.
429+ track of what generation the other database replica was at, and what
430+ the associated transaction id was. This is used to determine what data
431+ needs to be sent, and if two databases are claiming to be the same
432+ replica.
433
434 :param other_replica_uid: The identifier for the other replica.
435- :return: The generation we encountered during synchronization. If we've
436- never synchronized with the replica, this is 0.
437+ :return: (gen, trans_id) The generation and transaction id we
438+ encountered during synchronization. If we've never synchronized
439+ with the replica, this is (0, '').
440 """
441- raise NotImplementedError(self._get_sync_generation)
442+ raise NotImplementedError(self._get_sync_gen_info)
443
444- def _set_sync_generation(self, other_replica_uid, other_generation):
445+ def _set_sync_info(self, other_replica_uid, other_generation,
446+ other_transaction_id):
447 """Set the last-known generation for the other database replica.
448
449 We have just performed some synchronization, and we want to track what
450- generation the other replica was at. See also get_sync_generation.
451+ generation the other replica was at. See also _get_sync_gen_info.
452 :param other_replica_uid: The U1DB identifier for the other replica.
453 :param other_generation: The generation number for the other replica.
454+ :param other_transaction_id: The transaction id associated with the
455+ generation.
456 :return: None
457 """
458- raise NotImplementedError(self._set_sync_generation)
459+ raise NotImplementedError(self._set_sync_info)
460
461 def _put_doc_if_newer(self, doc, save_conflict, replica_uid=None,
462 replica_gen=None):
463@@ -338,11 +345,13 @@
464 :param source_replica_uid: Another replica which we might have
465 synchronized with in the past.
466 :return: (target_replica_uid, target_replica_generation,
467- source_replica_last_known_generation)
468+ source_replica_last_known_generation,
469+ source_replica_last_known_transaction_id)
470 """
471 raise NotImplementedError(self.get_sync_info)
472
473- def record_sync_info(self, source_replica_uid, source_replica_generation):
474+ def record_sync_info(self, source_replica_uid, source_replica_generation,
475+ source_replica_transaction_id):
476 """Record tip information for another replica.
477
478 After sync_exchange has been processed, the caller will have
479@@ -358,6 +367,8 @@
480 :param source_replica_uid: The identifier for the source replica.
481 :param source_replica_generation:
482 The database generation for the source replica.
483+ :param source_replica_transaction_id: The transaction id associated
484+ with the source replica generation.
485 :return: None
486 """
487 raise NotImplementedError(self.record_sync_info)
488
489=== modified file 'u1db/backends/__init__.py'
490--- u1db/backends/__init__.py 2012-05-17 14:44:25 +0000
491+++ u1db/backends/__init__.py 2012-05-18 09:33:18 +0000
492@@ -115,7 +115,7 @@
493 if save_conflict:
494 self._force_doc_sync_conflict(doc)
495 if replica_uid is not None and replica_gen is not None:
496- self._do_set_sync_generation(replica_uid, replica_gen)
497+ self._do_set_sync_info(replica_uid, replica_gen, 'T-sid')
498 return state, self._get_generation()
499
500 def _ensure_maximal_rev(self, cur_rev, extra_revs):
501
502=== modified file 'u1db/backends/dbschema.sql'
503--- u1db/backends/dbschema.sql 2012-05-17 13:59:05 +0000
504+++ u1db/backends/dbschema.sql 2012-05-18 09:33:18 +0000
505@@ -1,17 +1,17 @@
506 -- Database schema
507 CREATE TABLE transaction_log (
508 generation INTEGER PRIMARY KEY AUTOINCREMENT,
509- doc_id TEXT,
510- transaction_id TEXT
511+ doc_id TEXT NOT NULL,
512+ transaction_id TEXT NOT NULL
513 );
514 CREATE TABLE document (
515 doc_id TEXT PRIMARY KEY,
516- doc_rev TEXT,
517+ doc_rev TEXT NOT NULL,
518 content TEXT
519 );
520 CREATE TABLE document_fields (
521- doc_id TEXT,
522- field_name TEXT,
523+ doc_id TEXT NOT NULL,
524+ field_name TEXT NOT NULL,
525 value TEXT
526 );
527 CREATE INDEX document_fields_field_value_doc_idx
528
529=== modified file 'u1db/backends/inmemory.py'
530--- u1db/backends/inmemory.py 2012-05-17 14:44:25 +0000
531+++ u1db/backends/inmemory.py 2012-05-18 09:33:18 +0000
532@@ -45,16 +45,20 @@
533 # may be closing it, while another wants to inspect the results.
534 pass
535
536- def _get_sync_generation(self, other_replica_uid):
537- return self._other_generations.get(other_replica_uid, 0)
538-
539- def _set_sync_generation(self, other_replica_uid, other_generation):
540- self._do_set_sync_generation(other_replica_uid, other_generation)
541-
542- def _do_set_sync_generation(self, other_replica_uid, other_generation):
543+ def _get_sync_gen_info(self, other_replica_uid):
544+ return self._other_generations.get(other_replica_uid, (0, ''))
545+
546+ def _set_sync_info(self, other_replica_uid, other_generation,
547+ other_transaction_id):
548+ self._do_set_sync_info(other_replica_uid, other_generation,
549+ other_transaction_id)
550+
551+ def _do_set_sync_info(self, other_replica_uid, other_generation,
552+ other_transaction_id):
553 # TODO: to handle race conditions, we may want to check if the current
554 # value is greater than this new value.
555- self._other_generations[other_replica_uid] = other_generation
556+ self._other_generations[other_replica_uid] = (other_generation,
557+ other_transaction_id)
558
559 def get_sync_target(self):
560 return InMemorySyncTarget(self)
561@@ -203,7 +207,7 @@
562 generation = cur_generation
563 for doc_id, trans_id in reversed(relevant_tail):
564 if doc_id not in seen:
565- changes.append((doc_id, generation))
566+ changes.append((doc_id, generation, trans_id))
567 seen.add(doc_id)
568 generation -= 1
569 changes.reverse()
570@@ -334,12 +338,13 @@
571 class InMemorySyncTarget(CommonSyncTarget):
572
573 def get_sync_info(self, source_replica_uid):
574- source_gen = self._db._get_sync_generation(source_replica_uid)
575- return (
576- self._db._replica_uid, len(self._db._transaction_log), source_gen)
577+ source_gen, trans_id = self._db._get_sync_gen_info(source_replica_uid)
578+ return (self._db._replica_uid, len(self._db._transaction_log),
579+ source_gen, trans_id)
580
581- def record_sync_info(self, source_replica_uid, source_replica_generation):
582+ def record_sync_info(self, source_replica_uid, source_replica_generation,
583+ source_transaction_id):
584 if self._trace_hook:
585 self._trace_hook('record_sync_info')
586- self._db._set_sync_generation(source_replica_uid,
587- source_replica_generation)
588+ self._db._set_sync_info(source_replica_uid, source_replica_generation,
589+ source_transaction_id)
590
591=== modified file 'u1db/backends/sqlite_backend.py'
592--- u1db/backends/sqlite_backend.py 2012-05-17 15:13:42 +0000
593+++ u1db/backends/sqlite_backend.py 2012-05-18 09:33:18 +0000
594@@ -355,16 +355,17 @@
595
596 def whats_changed(self, old_generation=0):
597 c = self._db_handle.cursor()
598- c.execute("SELECT generation, doc_id FROM transaction_log"
599+ c.execute("SELECT generation, doc_id, transaction_id"
600+ " FROM transaction_log"
601 " WHERE generation > ? ORDER BY generation DESC",
602 (old_generation,))
603 results = c.fetchall()
604 cur_gen = old_generation
605 seen = set()
606 changes = []
607- for generation, doc_id in results:
608+ for generation, doc_id, trans_id in results:
609 if doc_id not in seen:
610- changes.append((doc_id, generation))
611+ changes.append((doc_id, generation, trans_id))
612 seen.add(doc_id)
613 if changes:
614 cur_gen = changes[0][1] # max generation
615@@ -409,26 +410,32 @@
616 this_doc.has_conflicts = True
617 return [this_doc] + conflict_docs
618
619- def _get_sync_generation(self, other_replica_uid):
620+ def _get_sync_gen_info(self, other_replica_uid):
621 c = self._db_handle.cursor()
622- c.execute("SELECT known_generation FROM sync_log"
623+ c.execute("SELECT known_generation, known_transaction_id FROM sync_log"
624 " WHERE replica_uid = ?",
625 (other_replica_uid,))
626 val = c.fetchone()
627 if val is None:
628 other_gen = 0
629+ trans_id = ''
630 else:
631 other_gen = val[0]
632- return other_gen
633+ trans_id = val[1]
634+ return other_gen, trans_id
635
636- def _set_sync_generation(self, other_replica_uid, other_generation):
637+ def _set_sync_info(self, other_replica_uid, other_generation,
638+ other_transaction_id):
639 with self._db_handle:
640- self._do_set_sync_generation(other_replica_uid, other_generation)
641+ self._do_set_sync_info(other_replica_uid, other_generation,
642+ other_transaction_id)
643
644- def _do_set_sync_generation(self, other_replica_uid, other_generation):
645+ def _do_set_sync_info(self, other_replica_uid, other_generation,
646+ other_transaction_id):
647 c = self._db_handle.cursor()
648 c.execute("INSERT OR REPLACE INTO sync_log VALUES (?, ?, ?)",
649- (other_replica_uid, other_generation, ''))
650+ (other_replica_uid, other_generation,
651+ other_transaction_id))
652
653 def _put_doc_if_newer(self, doc, save_conflict, replica_uid=None,
654 replica_gen=None):
655@@ -608,15 +615,16 @@
656 class SQLiteSyncTarget(CommonSyncTarget):
657
658 def get_sync_info(self, source_replica_uid):
659- source_gen = self._db._get_sync_generation(source_replica_uid)
660+ source_gen, trans_id = self._db._get_sync_gen_info(source_replica_uid)
661 my_gen = self._db._get_generation()
662- return self._db._replica_uid, my_gen, source_gen
663+ return self._db._replica_uid, my_gen, source_gen, trans_id
664
665- def record_sync_info(self, source_replica_uid, source_replica_generation):
666+ def record_sync_info(self, source_replica_uid, source_replica_generation,
667+ source_replica_transaction_id):
668 if self._trace_hook:
669 self._trace_hook('record_sync_info')
670- self._db._set_sync_generation(source_replica_uid,
671- source_replica_generation)
672+ self._db._set_sync_info(source_replica_uid, source_replica_generation,
673+ source_replica_transaction_id)
674
675
676 class SQLitePartialExpandDatabase(SQLiteDatabase):
677
678=== modified file 'u1db/remote/http_app.py'
679--- u1db/remote/http_app.py 2012-05-15 13:01:47 +0000
680+++ u1db/remote/http_app.py 2012-05-18 09:33:18 +0000
681@@ -290,12 +290,14 @@
682 self.responder.send_response_json(target_replica_uid=result[0],
683 target_replica_generation=result[1],
684 source_replica_uid=self.source_replica_uid,
685- source_replica_generation=result[2])
686+ source_replica_generation=result[2],
687+ source_transaction_id=result[3])
688
689 @http_method(generation=int,
690 content_as_args=True, no_query=True)
691- def put(self, generation):
692- self.target.record_sync_info(self.source_replica_uid, generation)
693+ def put(self, generation, transaction_id):
694+ self.target.record_sync_info(self.source_replica_uid, generation,
695+ transaction_id)
696 self.responder.send_response_json(ok=True)
697
698 # Implements the same logic as LocalSyncTarget.sync_exchange
699
700=== modified file 'u1db/remote/http_target.py'
701--- u1db/remote/http_target.py 2012-05-13 09:56:59 +0000
702+++ u1db/remote/http_target.py 2012-05-18 09:33:18 +0000
703@@ -42,12 +42,14 @@
704 self._ensure_connection()
705 res, _ = self._request_json('GET', ['sync-from', source_replica_uid])
706 return (res['target_replica_uid'], res['target_replica_generation'],
707- res['source_replica_generation'])
708+ res['source_replica_generation'], res['source_transaction_id'])
709
710- def record_sync_info(self, source_replica_uid, source_replica_generation):
711+ def record_sync_info(self, source_replica_uid, source_replica_generation,
712+ source_transaction_id):
713 self._ensure_connection()
714 self._request_json('PUT', ['sync-from', source_replica_uid], {},
715- {'generation': source_replica_generation})
716+ {'generation': source_replica_generation,
717+ 'transaction_id': source_transaction_id})
718
719 def _parse_sync_stream(self, data, return_doc_cb):
720 parts = data.splitlines() # one at a time
721
722=== modified file 'u1db/sync.py'
723--- u1db/sync.py 2012-05-17 13:36:08 +0000
724+++ u1db/sync.py 2012-05-18 09:33:18 +0000
725@@ -86,28 +86,28 @@
726 if (cur_gen == start_generation + self.num_inserted
727 and self.num_inserted > 0):
728 self.sync_target.record_sync_info(self.source._replica_uid,
729- cur_gen)
730+ cur_gen, 'T-sid')
731
732 def sync(self, callback=None):
733 """Synchronize documents between source and target."""
734 sync_target = self.sync_target
735 # get target identifier, its current generation,
736 # and its last-seen database generation for this source
737- (self.target_replica_uid, target_gen,
738- target_my_gen) = sync_target.get_sync_info(self.source._replica_uid)
739+ (self.target_replica_uid, target_gen, target_my_gen,
740+ target_my_trans_id) = sync_target.get_sync_info(self.source._replica_uid)
741 # what's changed since that generation and this current gen
742 my_gen, changes = self.source.whats_changed(target_my_gen)
743
744 # this source last-seen database generation for the target
745- target_last_known_gen = self.source._get_sync_generation(
746+ target_last_known_gen, target_trans_id = self.source._get_sync_gen_info(
747 self.target_replica_uid)
748 if not changes and target_last_known_gen == target_gen:
749 return my_gen
750- changed_doc_ids = [doc_id for doc_id, _ in changes]
751+ changed_doc_ids = [doc_id for doc_id, _, _ in changes]
752 # prepare to send all the changed docs
753 docs_to_send = self.source.get_docs(changed_doc_ids,
754 check_for_conflicts=False)
755- docs_by_generation = zip(docs_to_send, (gen for _, gen in changes))
756+ docs_by_generation = zip(docs_to_send, (gen for _, gen, _ in changes))
757
758 # exchange documents and try to insert the returned ones with
759 # the target, return target synced-up-to gen
760@@ -115,7 +115,7 @@
761 self.source._replica_uid, target_last_known_gen,
762 return_doc_cb=self._insert_doc_from_target)
763 # record target synced-up-to generation including applying what we sent
764- self.source._set_sync_generation(self.target_replica_uid, new_gen)
765+ self.source._set_sync_info(self.target_replica_uid, new_gen, 'T-sid')
766
767 # if gapless record current reached generation with target
768 self._record_sync_info_with_the_target(my_gen)
769@@ -206,10 +206,10 @@
770 self.new_gen = gen
771 seen_ids = self.seen_ids
772 # changed docs that weren't superseded by or converged with
773- self.changes_to_return = [(doc_id, gen) for (doc_id, gen) in changes
774- if doc_id not in seen_ids or
775- # there was a subsequent update
776- seen_ids.get(doc_id) < gen]
777+ self.changes_to_return = [(doc_id, gen) for (doc_id, gen, _) in changes
778+ # there was a subsequent update
779+ if doc_id not in seen_ids or
780+ seen_ids.get(doc_id) < gen]
781 return self.new_gen
782
783 def return_docs(self, return_doc_cb):
784
785=== modified file 'u1db/tests/__init__.py'
786--- u1db/tests/__init__.py 2012-05-17 15:13:42 +0000
787+++ u1db/tests/__init__.py 2012-05-18 09:33:18 +0000
788@@ -177,6 +177,10 @@
789 seen_transactions.add(transaction_id)
790 self.assertEqual(doc_ids, just_ids)
791
792+ def getLastTransId(self, db):
793+ """Return the transaction id for the last database update."""
794+ return self.db._get_transaction_log()[-1][-1]
795+
796
797 class ServerStateForTests(server_state.ServerState):
798 """Used in the test suite, so we don't have to touch disk, etc."""
799
800=== modified file 'u1db/tests/c_backend_wrapper.pyx'
801--- u1db/tests/c_backend_wrapper.pyx 2012-05-17 15:13:42 +0000
802+++ u1db/tests/c_backend_wrapper.pyx 2012-05-18 09:33:18 +0000
803@@ -61,8 +61,6 @@
804 ctypedef int (*u1db_key_callback)(void *context, char *key)
805 ctypedef int (*u1db_doc_gen_callback)(void *context,
806 u1db_document *doc, int gen)
807- ctypedef int (*u1db_doc_id_gen_callback)(void *context,
808- const_char_ptr doc_id, int gen)
809 ctypedef int (*u1db_trans_info_callback)(void *context,
810 const_char_ptr doc_id, int gen, const_char_ptr trans_id)
811
812@@ -85,7 +83,7 @@
813 int n_revs, const_char_ptr *revs)
814 int u1db_delete_doc(u1database *db, u1db_document *doc)
815 int u1db_whats_changed(u1database *db, int *gen, void *context,
816- u1db_doc_id_gen_callback cb)
817+ u1db_trans_info_callback cb)
818 int u1db__get_transaction_log(u1database *db, void *context,
819 u1db_trans_info_callback cb)
820 int u1db_get_doc_conflicts(u1database *db, char *doc_id, void *context,
821@@ -156,9 +154,10 @@
822 ctypedef struct u1db_sync_target:
823 int (*get_sync_info)(u1db_sync_target *st,
824 char *source_replica_uid,
825- const_char_ptr *st_replica_uid, int *st_gen, int *source_gen) nogil
826+ const_char_ptr *st_replica_uid, int *st_gen, int *source_gen,
827+ char **source_trans_id) nogil
828 int (*record_sync_info)(u1db_sync_target *st,
829- char *source_replica_uid, int source_gen) nogil
830+ char *source_replica_uid, int source_gen, char *trans_id) nogil
831 int (*sync_exchange)(u1db_sync_target *st,
832 char *source_replica_uid, int n_docs,
833 u1db_document **docs, int *generations,
834@@ -191,10 +190,10 @@
835 char *content, int has_conflicts)
836 int u1db__generate_hex_uuid(char *)
837
838- int u1db__get_sync_generation(u1database *db, char *replica_uid,
839- int *generation)
840- int u1db__set_sync_generation(u1database *db, char *replica_uid,
841- int generation)
842+ int u1db__get_sync_gen_info(u1database *db, char *replica_uid,
843+ int *generation, char **trans_id)
844+ int u1db__set_sync_info(u1database *db, char *replica_uid, int generation,
845+ char *trans_id)
846 int u1db__sync_get_machine_info(u1database *db, char *other_replica_uid,
847 int *other_db_rev, char **my_replica_uid,
848 int *my_db_rev)
849@@ -253,14 +252,6 @@
850 from sqlite3 import dbapi2
851
852
853-cdef int _append_doc_gen_to_list(void *context, const_char_ptr doc_id,
854- int generation) with gil:
855- a_list = <object>(context)
856- doc = doc_id
857- a_list.append((doc, generation))
858- return 0
859-
860-
861 cdef int _append_trans_info_to_list(void *context, const_char_ptr doc_id,
862 int generation,
863 const_char_ptr trans_id) with gil:
864@@ -665,22 +656,27 @@
865 def get_sync_info(self, source_replica_uid):
866 cdef const_char_ptr st_replica_uid = NULL
867 cdef int st_gen = 0, source_gen = 0, status
868+ cdef char *trans_id = NULL
869
870 self._check()
871 assert self._st.get_sync_info != NULL, "get_sync_info is NULL?"
872 with nogil:
873 status = self._st.get_sync_info(self._st, source_replica_uid,
874- &st_replica_uid, &st_gen, &source_gen)
875+ &st_replica_uid, &st_gen, &source_gen, &trans_id)
876 handle_status("get_sync_info", status)
877- return (safe_str(st_replica_uid), st_gen, source_gen)
878+ res_trans_id = None
879+ if trans_id != NULL:
880+ res_trans_id = trans_id
881+ free(trans_id)
882+ return (safe_str(st_replica_uid), st_gen, source_gen, res_trans_id)
883
884- def record_sync_info(self, source_replica_uid, source_gen):
885+ def record_sync_info(self, source_replica_uid, source_gen, source_trans_id):
886 cdef int status
887 self._check()
888 assert self._st.record_sync_info != NULL, "record_sync_info is NULL?"
889 with nogil:
890 status = self._st.record_sync_info(self._st, source_replica_uid,
891- source_gen)
892+ source_gen, source_trans_id)
893 handle_status("record_sync_info", status)
894
895 def _get_sync_exchange(self, source_replica_uid, source_gen):
896@@ -952,7 +948,7 @@
897 c_generation = generation
898 handle_status("whats_changed",
899 u1db_whats_changed(self._db, &c_generation, <void*>a_list,
900- _append_doc_gen_to_list))
901+ _append_trans_info_to_list))
902 return c_generation, a_list
903
904 def _get_transaction_log(self):
905@@ -968,16 +964,22 @@
906 u1db__get_generation(self._db, &generation))
907 return generation
908
909- def _get_sync_generation(self, replica_uid):
910- cdef int generation
911-
912- handle_status("_get_sync_generation",
913- u1db__get_sync_generation(self._db, replica_uid, &generation))
914- return generation
915-
916- def _set_sync_generation(self, replica_uid, generation):
917- handle_status("_set_sync_generation",
918- u1db__set_sync_generation(self._db, replica_uid, generation))
919+ def _get_sync_gen_info(self, replica_uid):
920+ cdef int generation, status
921+ cdef char *trans_id = NULL
922+
923+ status = u1db__get_sync_gen_info(self._db, replica_uid, &generation,
924+ &trans_id)
925+ handle_status("_get_sync_gen_info", status)
926+ raw_trans_id = None
927+ if trans_id != NULL:
928+ raw_trans_id = trans_id
929+ free(trans_id)
930+ return generation, raw_trans_id
931+
932+ def _set_sync_info(self, replica_uid, generation, trans_id):
933+ handle_status("_set_sync_info",
934+ u1db__set_sync_info(self._db, replica_uid, generation, trans_id))
935
936 def _sync_exchange(self, docs_info, from_replica_uid, from_machine_rev,
937 last_known_rev):
938
939=== modified file 'u1db/tests/test_backends.py'
940--- u1db/tests/test_backends.py 2012-05-17 14:44:25 +0000
941+++ u1db/tests/test_backends.py 2012-05-18 09:33:18 +0000
942@@ -296,19 +296,19 @@
943
944 def test_put_doc_if_newer_replica_uid(self):
945 doc1 = self.db.create_doc(simple_doc)
946- self.db._set_sync_generation('other', 1)
947+ self.db._set_sync_info('other', 1, 'T-sid')
948 doc2 = self.make_document(doc1.doc_id, doc1.rev + '|other:1',
949 nested_doc)
950 self.assertEqual('inserted',
951 self.db._put_doc_if_newer(doc2, save_conflict=False,
952 replica_uid='other', replica_gen=2)[0])
953- self.assertEqual(2, self.db._get_sync_generation('other'))
954+ self.assertEqual(2, self.db._get_sync_gen_info('other')[0])
955 # Compare to the old rev, should be superseded
956 doc2 = self.make_document(doc1.doc_id, doc1.rev, nested_doc)
957 self.assertEqual('superseded',
958 self.db._put_doc_if_newer(doc2, save_conflict=False,
959 replica_uid='other', replica_gen=3)[0])
960- self.assertEqual(3, self.db._get_sync_generation('other'))
961+ self.assertEqual(3, self.db._get_sync_gen_info('other')[0])
962 # A conflict that isn't saved still records the sync gen, because we
963 # don't need to see it again
964 doc2 = self.make_document(doc1.doc_id, doc1.rev + '|fourth:1',
965@@ -316,12 +316,13 @@
966 self.assertEqual('conflicted',
967 self.db._put_doc_if_newer(doc2, save_conflict=False,
968 replica_uid='other', replica_gen=4)[0])
969- self.assertEqual(4, self.db._get_sync_generation('other'))
970+ self.assertEqual(4, self.db._get_sync_gen_info('other')[0])
971
972- def test__get_sync_generation(self):
973- self.assertEqual(0, self.db._get_sync_generation('other-db'))
974- self.db._set_sync_generation('other-db', 2)
975- self.assertEqual(2, self.db._get_sync_generation('other-db'))
976+ def test__get_sync_gen_info(self):
977+ self.assertEqual((0, ''), self.db._get_sync_gen_info('other-db'))
978+ self.db._set_sync_info('other-db', 2, 'T-transaction')
979+ self.assertEqual((2, 'T-transaction'),
980+ self.db._get_sync_gen_info('other-db'))
981
982 def test_put_updates_transaction_log(self):
983 doc = self.db.create_doc(simple_doc)
984@@ -329,13 +330,17 @@
985 doc.content = '{"something": "else"}'
986 self.db.put_doc(doc)
987 self.assertTransactionLog([doc.doc_id, doc.doc_id], self.db)
988- self.assertEqual((2, [(doc.doc_id, 2)]), self.db.whats_changed())
989+ last_trans_id = self.getLastTransId(self.db)
990+ self.assertEqual((2, [(doc.doc_id, 2, last_trans_id)]),
991+ self.db.whats_changed())
992
993 def test_delete_updates_transaction_log(self):
994 doc = self.db.create_doc(simple_doc)
995 db_gen, _ = self.db.whats_changed()
996 self.db.delete_doc(doc)
997- self.assertEqual((2, [(doc.doc_id, 2)]), self.db.whats_changed(db_gen))
998+ last_trans_id = self.getLastTransId(self.db)
999+ self.assertEqual((2, [(doc.doc_id, 2, last_trans_id)]),
1000+ self.db.whats_changed(db_gen))
1001
1002 def test_whats_changed_initial_database(self):
1003 self.assertEqual((0, []), self.db.whats_changed())
1004@@ -344,7 +349,9 @@
1005 doc = self.db.create_doc(simple_doc)
1006 doc.content = '{"new": "contents"}'
1007 self.db.put_doc(doc)
1008- self.assertEqual((2, [(doc.doc_id, 2)]), self.db.whats_changed())
1009+ last_trans_id = self.getLastTransId(self.db)
1010+ self.assertEqual((2, [(doc.doc_id, 2, last_trans_id)]),
1011+ self.db.whats_changed())
1012 self.assertEqual((2, []), self.db.whats_changed(2))
1013
1014 def test_whats_changed_returns_last_edits_ascending(self):
1015@@ -352,15 +359,19 @@
1016 doc1 = self.db.create_doc(simple_doc)
1017 doc.content = '{"new": "contents"}'
1018 self.db.delete_doc(doc1)
1019+ delete_trans_id = self.getLastTransId(self.db)
1020 self.db.put_doc(doc)
1021- self.assertEqual((4, [(doc1.doc_id, 3), (doc.doc_id, 4)]),
1022+ put_trans_id = self.getLastTransId(self.db)
1023+ self.assertEqual((4, [(doc1.doc_id, 3, delete_trans_id),
1024+ (doc.doc_id, 4, put_trans_id)]),
1025 self.db.whats_changed())
1026
1027- def test_whats_changed_doesnt_includ_old_gen(self):
1028+ def test_whats_changed_doesnt_include_old_gen(self):
1029 self.db.create_doc(simple_doc)
1030 self.db.create_doc(simple_doc)
1031 doc2 = self.db.create_doc(simple_doc)
1032- self.assertEqual((3, [(doc2.doc_id, 3)]),
1033+ last_trans_id = self.getLastTransId(self.db)
1034+ self.assertEqual((3, [(doc2.doc_id, 3, last_trans_id)]),
1035 self.db.whats_changed(2))
1036
1037
1038@@ -568,7 +579,7 @@
1039
1040 def test_put_doc_if_newer_replica_uid(self):
1041 doc1 = self.db.create_doc(simple_doc)
1042- self.db._set_sync_generation('other', 1)
1043+ self.db._set_sync_info('other', 1, 'T-sid')
1044 doc2 = self.make_document(doc1.doc_id, doc1.rev + '|other:1',
1045 nested_doc)
1046 self.db._put_doc_if_newer(doc2, save_conflict=True,
1047@@ -579,7 +590,7 @@
1048 self.assertEqual('conflicted',
1049 self.db._put_doc_if_newer(doc2, save_conflict=True,
1050 replica_uid='other', replica_gen=3)[0])
1051- self.assertEqual(3, self.db._get_sync_generation('other'))
1052+ self.assertEqual(3, self.db._get_sync_gen_info('other')[0])
1053
1054 def test_put_refuses_to_update_conflicted(self):
1055 doc1 = self.db.create_doc(simple_doc)
1056
1057=== modified file 'u1db/tests/test_c_backend.py'
1058--- u1db/tests/test_c_backend.py 2012-05-17 13:30:51 +0000
1059+++ u1db/tests/test_c_backend.py 2012-05-18 09:33:18 +0000
1060@@ -201,7 +201,8 @@
1061 exc.insert_doc_from_source(doc, 10)
1062 self.assertGetDoc(self.db, 'doc-id', 'replica:1', tests.simple_doc,
1063 False)
1064- self.assertEqual(10, self.db._get_sync_generation('source-uid'))
1065+ self.assertEqual((10, 'T-sid'),
1066+ self.db._get_sync_gen_info('source-uid'))
1067 self.assertEqual(['doc-id'], exc.get_seen_ids())
1068
1069 def test_sync_exchange_conflicted_doc(self):
1070
1071=== modified file 'u1db/tests/test_http_app.py'
1072--- u1db/tests/test_http_app.py 2012-05-17 13:36:08 +0000
1073+++ u1db/tests/test_http_app.py 2012-05-18 09:33:18 +0000
1074@@ -664,24 +664,26 @@
1075 simplejson.loads(resp.body))
1076
1077 def test_get_sync_info(self):
1078- self.db0._set_sync_generation('other-id', 1)
1079+ self.db0._set_sync_info('other-id', 1, 'T-transid')
1080 resp = self.app.get('/db0/sync-from/other-id')
1081 self.assertEqual(200, resp.status)
1082 self.assertEqual('application/json', resp.header('content-type'))
1083 self.assertEqual(dict(target_replica_uid='db0',
1084 target_replica_generation=0,
1085 source_replica_uid='other-id',
1086- source_replica_generation=1),
1087+ source_replica_generation=1,
1088+ source_transaction_id='T-transid'),
1089 simplejson.loads(resp.body))
1090
1091 def test_record_sync_info(self):
1092 resp = self.app.put('/db0/sync-from/other-id',
1093- params='{"generation": 2}',
1094- headers={'content-type': 'application/json'})
1095+ params='{"generation": 2, "transaction_id": "T-transid"}',
1096+ headers={'content-type': 'application/json'})
1097 self.assertEqual(200, resp.status)
1098 self.assertEqual('application/json', resp.header('content-type'))
1099 self.assertEqual({'ok': True}, simplejson.loads(resp.body))
1100- self.assertEqual(self.db0._get_sync_generation('other-id'), 2)
1101+ self.assertEqual((2, 'T-transid'),
1102+ self.db0._get_sync_gen_info('other-id'))
1103
1104 def test_sync_exchange_send(self):
1105 entries = {
1106@@ -692,16 +694,15 @@
1107 }
1108
1109 gens = []
1110- _do_set_sync_generation = self.db0._do_set_sync_generation
1111- def set_sync_generation_witness(other_uid, other_gen):
1112+ _do_set_sync_info = self.db0._do_set_sync_info
1113+ def set_sync_generation_witness(other_uid, other_gen, other_trans_id):
1114 gens.append((other_uid, other_gen))
1115- _do_set_sync_generation(other_uid, other_gen)
1116+ _do_set_sync_info(other_uid, other_gen, other_trans_id)
1117 self.assertGetDoc(self.db0, entries[other_gen]['id'],
1118 entries[other_gen]['rev'],
1119 entries[other_gen]['content'], False)
1120
1121- self.patch(self.db0, '_do_set_sync_generation',
1122- set_sync_generation_witness)
1123+ self.patch(self.db0, '_do_set_sync_info', set_sync_generation_witness)
1124
1125 args = dict(last_known_generation=0)
1126 body = ("[\r\n" +
1127
1128=== modified file 'u1db/tests/test_https.py'
1129--- u1db/tests/test_https.py 2012-05-17 15:13:31 +0000
1130+++ u1db/tests/test_https.py 2012-05-18 09:33:18 +0000
1131@@ -83,8 +83,8 @@
1132 db = self.request_state._create_database('test')
1133 self.patch(http_client, 'CA_CERTS', self.cacert_pem)
1134 remote_target = self.getSyncTarget('localhost', 'test')
1135- remote_target.record_sync_info('other-id', 2)
1136- self.assertEqual(db._get_sync_generation('other-id'), 2)
1137+ remote_target.record_sync_info('other-id', 2, 'T-id')
1138+ self.assertEqual((2, 'T-id'), db._get_sync_gen_info('other-id'))
1139
1140 def test_cannot_verify_cert(self):
1141 if not sys.platform.startswith('linux'):
1142
1143=== modified file 'u1db/tests/test_remote_sync_target.py'
1144--- u1db/tests/test_remote_sync_target.py 2012-05-17 13:36:08 +0000
1145+++ u1db/tests/test_remote_sync_target.py 2012-05-18 09:33:18 +0000
1146@@ -164,17 +164,17 @@
1147 def test_get_sync_info(self):
1148 self.startServer()
1149 db = self.request_state._create_database('test')
1150- db._set_sync_generation('other-id', 1)
1151+ db._set_sync_info('other-id', 1, 'T-transid')
1152 remote_target = self.getSyncTarget('test')
1153- self.assertEqual(('test', 0, 1),
1154+ self.assertEqual(('test', 0, 1, 'T-transid'),
1155 remote_target.get_sync_info('other-id'))
1156
1157 def test_record_sync_info(self):
1158 self.startServer()
1159 db = self.request_state._create_database('test')
1160 remote_target = self.getSyncTarget('test')
1161- remote_target.record_sync_info('other-id', 2)
1162- self.assertEqual(db._get_sync_generation('other-id'), 2)
1163+ remote_target.record_sync_info('other-id', 2, 'T-transid')
1164+ self.assertEqual((2, 'T-transid'), db._get_sync_gen_info('other-id'))
1165
1166 def test_sync_exchange_send(self):
1167 self.startServer()
1168@@ -222,7 +222,7 @@
1169 return_doc_cb=receive_doc)
1170 self.assertGetDoc(db, 'doc-here', 'replica:1', '{"value": "here"}',
1171 False)
1172- self.assertEqual(10, db._get_sync_generation('replica'))
1173+ self.assertEqual((10, 'T-sid'), db._get_sync_gen_info('replica'))
1174 self.assertEqual([], other_changes)
1175 # retry
1176 trigger_ids = []
1177@@ -232,7 +232,7 @@
1178 return_doc_cb=receive_doc)
1179 self.assertGetDoc(db, 'doc-here2', 'replica:1', '{"value": "here2"}',
1180 False)
1181- self.assertEqual(11, db._get_sync_generation('replica'))
1182+ self.assertEqual((11, 'T-sid'), db._get_sync_gen_info('replica'))
1183 self.assertEqual(2, new_gen)
1184 # bounced back to us
1185 self.assertEqual([('doc-here', 'replica:1', '{"value": "here"}', 1)],
1186
1187=== modified file 'u1db/tests/test_sync.py'
1188--- u1db/tests/test_sync.py 2012-05-17 14:44:25 +0000
1189+++ u1db/tests/test_sync.py 2012-05-18 09:33:18 +0000
1190@@ -141,17 +141,18 @@
1191 self.assertIsNot(None, self.st)
1192
1193 def test_get_sync_info(self):
1194- self.assertEqual(('test', 0, 0), self.st.get_sync_info('other'))
1195+ self.assertEqual(('test', 0, 0, ''), self.st.get_sync_info('other'))
1196
1197 def test_create_doc_updates_sync_info(self):
1198- self.assertEqual(('test', 0, 0), self.st.get_sync_info('other'))
1199+ self.assertEqual(('test', 0, 0, ''), self.st.get_sync_info('other'))
1200 doc = self.db.create_doc(simple_doc)
1201- self.assertEqual(('test', 1, 0), self.st.get_sync_info('other'))
1202+ self.assertEqual(('test', 1, 0, ''), self.st.get_sync_info('other'))
1203
1204 def test_record_sync_info(self):
1205- self.assertEqual(('test', 0, 0), self.st.get_sync_info('replica'))
1206- self.st.record_sync_info('replica', 10)
1207- self.assertEqual(('test', 0, 10), self.st.get_sync_info('replica'))
1208+ self.assertEqual(('test', 0, 0, ''), self.st.get_sync_info('replica'))
1209+ self.st.record_sync_info('replica', 10, 'T-transid')
1210+ self.assertEqual(('test', 0, 10, 'T-transid'),
1211+ self.st.get_sync_info('replica'))
1212
1213 def test_sync_exchange(self):
1214 docs_by_gen = [
1215@@ -162,7 +163,7 @@
1216 self.assertGetDoc(self.db, 'doc-id', 'replica:1', simple_doc, False)
1217 self.assertTransactionLog(['doc-id'], self.db)
1218 self.assertEqual(([], 1), (self.other_changes, new_gen))
1219- self.assertEqual(10, self.st.get_sync_info('replica')[-1])
1220+ self.assertEqual(10, self.st.get_sync_info('replica')[2])
1221
1222 def test_sync_exchange_deleted(self):
1223 doc = self.db.create_doc('{}')
1224@@ -175,7 +176,7 @@
1225 self.assertGetDoc(self.db, doc.doc_id, edit_rev, None, False)
1226 self.assertTransactionLog([doc.doc_id, doc.doc_id], self.db)
1227 self.assertEqual(([], 2), (self.other_changes, new_gen))
1228- self.assertEqual(10, self.st.get_sync_info('replica')[-1])
1229+ self.assertEqual(10, self.st.get_sync_info('replica')[2])
1230
1231 def test_sync_exchange_push_many(self):
1232 docs_by_gen = [
1233@@ -188,7 +189,7 @@
1234 self.assertGetDoc(self.db, 'doc-id2', 'replica:1', nested_doc, False)
1235 self.assertTransactionLog(['doc-id', 'doc-id2'], self.db)
1236 self.assertEqual(([], 2), (self.other_changes, new_gen))
1237- self.assertEqual(11, self.st.get_sync_info('replica')[-1])
1238+ self.assertEqual(11, self.st.get_sync_info('replica')[2])
1239
1240 def test_sync_exchange_refuses_conflicts(self):
1241 doc = self.db.create_doc(simple_doc)
1242@@ -345,7 +346,7 @@
1243 self.assertGetDoc(self.db, doc.doc_id, doc.rev, simple_doc, False)
1244 self.assertTransactionLog([doc.doc_id], self.db)
1245 self.assertEqual(([], 1), (self.other_changes, new_gen))
1246- self.assertEqual(10, self.st.get_sync_info(db2._replica_uid)[-1])
1247+ self.assertEqual(10, self.st.get_sync_info(db2._replica_uid)[2])
1248
1249 def test__set_trace_hook(self):
1250 called = []
1251@@ -353,7 +354,7 @@
1252 called.append(state)
1253 self.set_trace_hook(cb)
1254 self.st.sync_exchange([], 'replica', 0, self.receive_doc)
1255- self.st.record_sync_info('replica', 0)
1256+ self.st.record_sync_info('replica', 0, 'T-sid')
1257 self.assertEqual(['before whats_changed',
1258 'after whats_changed',
1259 'before get_docs',
1260@@ -410,8 +411,8 @@
1261
1262 def test_sync_tracks_db_generation_of_other(self):
1263 self.assertEqual(0, self.sync(self.db1, self.db2))
1264- self.assertEqual(0, self.db1._get_sync_generation('test2'))
1265- self.assertEqual(0, self.db2._get_sync_generation('test1'))
1266+ self.assertEqual((0, ''), self.db1._get_sync_gen_info('test2'))
1267+ self.assertEqual((0, ''), self.db2._get_sync_gen_info('test1'))
1268 self.assertLastExchangeLog(self.db2,
1269 {'receive': {'docs': [], 'last_known_gen': 0},
1270 'return': {'docs': [], 'last_gen': 0}})
1271@@ -420,8 +421,8 @@
1272 doc = self.db1.create_doc(simple_doc)
1273 self.assertEqual(1, self.sync(self.db1, self.db2))
1274 self.assertGetDoc(self.db2, doc.doc_id, doc.rev, simple_doc, False)
1275- self.assertEqual(1, self.db1._get_sync_generation('test2'))
1276- self.assertEqual(1, self.db2._get_sync_generation('test1'))
1277+ self.assertEqual(1, self.db1._get_sync_gen_info('test2')[0])
1278+ self.assertEqual(1, self.db2._get_sync_gen_info('test1')[0])
1279 self.assertLastExchangeLog(self.db2,
1280 {'receive': {'docs': [(doc.doc_id, doc.rev)],
1281 'source_uid': 'test1',
1282@@ -433,8 +434,8 @@
1283 self.db1.create_index('test-idx', ['key'])
1284 self.assertEqual(0, self.sync(self.db1, self.db2))
1285 self.assertGetDoc(self.db1, doc.doc_id, doc.rev, simple_doc, False)
1286- self.assertEqual(1, self.db1._get_sync_generation('test2'))
1287- self.assertEqual(1, self.db2._get_sync_generation('test1'))
1288+ self.assertEqual(1, self.db1._get_sync_gen_info('test2')[0])
1289+ self.assertEqual(1, self.db2._get_sync_gen_info('test1')[0])
1290 self.assertLastExchangeLog(self.db2,
1291 {'receive': {'docs': [], 'last_known_gen': 0},
1292 'return': {'docs': [(doc.doc_id, doc.rev)],
1293@@ -459,11 +460,11 @@
1294 {'receive': {'docs': [], 'last_known_gen': 0},
1295 'return': {'docs': [(doc.doc_id, doc.rev)],
1296 'last_gen': 1}})
1297- self.assertEqual(1, self.db1._get_sync_generation('test2'))
1298+ self.assertEqual(1, self.db1._get_sync_gen_info('test2')[0])
1299 # c2 should not have gotten a '_record_sync_info' call, because the
1300 # local database had been updated more than just by the messages
1301 # returned from c2.
1302- self.assertEqual(0, self.db2._get_sync_generation('test1'))
1303+ self.assertEqual((0, ''), self.db2._get_sync_gen_info('test1'))
1304
1305 def test_sync_doesnt_update_other_if_nothing_pulled(self):
1306 doc = self.db1.create_doc(simple_doc)
1307@@ -474,7 +475,7 @@
1308 self.assertEqual(1, self.sync(self.db1, self.db2,
1309 trace_hook=no_record_sync_info))
1310 self.assertEqual(1,
1311- self.db2._get_sync_generation(self.db1._replica_uid))
1312+ self.db2._get_sync_gen_info(self.db1._replica_uid)[0])
1313
1314 def test_sync_ignores_convergence(self):
1315 doc = self.db1.create_doc(simple_doc)
1316@@ -707,20 +708,20 @@
1317 self.assertEqual(2, len(self.db2._get_transaction_log()))
1318 progress1 = []
1319 progress2 = []
1320- _do_set_sync_generation1 = self.db1._do_set_sync_generation
1321- def set_sync_generation_witness1(other_uid, other_gen):
1322+ _do_set_sync_info = self.db1._do_set_sync_info
1323+ def set_sync_generation_witness1(other_uid, other_gen, trans_id):
1324 progress1.append((other_uid, other_gen,
1325 [d for d, t in self.db1._get_transaction_log()[2:]]))
1326- _do_set_sync_generation1(other_uid, other_gen)
1327- self.patch(self.db1, '_do_set_sync_generation',
1328+ _do_set_sync_info(other_uid, other_gen, trans_id)
1329+ self.patch(self.db1, '_do_set_sync_info',
1330 set_sync_generation_witness1)
1331
1332- _do_set_sync_generation2 = self.db2._do_set_sync_generation
1333- def set_sync_generation_witness2(other_uid, other_gen):
1334+ _do_set_sync_info2 = self.db2._do_set_sync_info
1335+ def set_sync_generation_witness2(other_uid, other_gen, trans_id):
1336 progress2.append((other_uid, other_gen,
1337 [d for d, t in self.db2._get_transaction_log()[2:]]))
1338- _do_set_sync_generation2(other_uid, other_gen)
1339- self.patch(self.db2, '_do_set_sync_generation',
1340+ _do_set_sync_info2(other_uid, other_gen, trans_id)
1341+ self.patch(self.db2, '_do_set_sync_info',
1342 set_sync_generation_witness2)
1343
1344 db2_url = self.getURL('test2')

Subscribers

People subscribed via source and target branches