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

Proposed by John A Meinel
Status: Merged
Merged at revision: 294
Proposed branch: lp:~jameinel/u1db/whats_changed_999574
Merge into: lp:u1db
Prerequisite: lp:~jameinel/u1db/sync_info_999574
Diff against target: 292 lines (+55/-45)
10 files modified
include/u1db/u1db.h (+8/-7)
src/u1db.c (+8/-6)
src/u1db_sync_target.c (+2/-1)
u1db/__init__.py (+4/-3)
u1db/backends/inmemory.py (+1/-1)
u1db/backends/sqlite_backend.py (+4/-3)
u1db/sync.py (+6/-6)
u1db/tests/__init__.py (+4/-0)
u1db/tests/c_backend_wrapper.pyx (+2/-12)
u1db/tests/test_backends.py (+16/-6)
To merge this branch: bzr merge lp:~jameinel/u1db/whats_changed_999574
Reviewer Review Type Date Requested Status
Eric Casteleijn (community) Approve
Review via email: mp+106340@code.launchpad.net

This proposal supersedes 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.)

(resubmitting because I forgot to set the prereq branch.)

To post a comment you must log in.
Revision history for this message
Eric Casteleijn (thisfred) wrote :

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/u1db/u1db.h'
2--- include/u1db/u1db.h 2012-05-17 15:13:42 +0000
3+++ include/u1db/u1db.h 2012-05-18 09:33:19 +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 'src/u1db.c'
30--- src/u1db.c 2012-05-18 09:33:19 +0000
31+++ src/u1db.c 2012-05-18 09:33:19 +0000
32@@ -1108,7 +1108,7 @@
33
34 int
35 u1db_whats_changed(u1database *db, int *gen, void *context,
36- int (*cb)(void *, const char *doc_id, int gen))
37+ u1db_trans_info_callback cb)
38 {
39 int status;
40 sqlite3_stmt *statement;
41@@ -1116,8 +1116,8 @@
42 return -1; // Bad parameters
43 }
44 status = sqlite3_prepare_v2(db->sql_handle,
45- "SELECT max(generation) as g, doc_id FROM transaction_log"
46- " WHERE generation > ?"
47+ "SELECT max(generation) as g, doc_id, transaction_id"
48+ " FROM transaction_log WHERE generation > ?"
49 " GROUP BY doc_id ORDER BY g",
50 -1, &statement, NULL);
51 if (status != SQLITE_OK) {
52@@ -1131,13 +1131,15 @@
53 status = sqlite3_step(statement);
54 while (status == SQLITE_ROW) {
55 int local_gen;
56- char *doc_id;
57+ const char *doc_id;
58+ const char *trans_id;
59 local_gen = sqlite3_column_int(statement, 0);
60 if (local_gen > *gen) {
61 *gen = local_gen;
62 }
63- doc_id = (char *)sqlite3_column_text(statement, 1);
64- cb(context, doc_id, local_gen);
65+ doc_id = (const char *)sqlite3_column_text(statement, 1);
66+ trans_id = (const char *)sqlite3_column_text(statement, 2);
67+ cb(context, doc_id, local_gen, trans_id);
68 status = sqlite3_step(statement);
69 }
70 if (status == SQLITE_DONE) {
71
72=== modified file 'src/u1db_sync_target.c'
73--- src/u1db_sync_target.c 2012-05-18 09:33:19 +0000
74+++ src/u1db_sync_target.c 2012-05-18 09:33:19 +0000
75@@ -304,7 +304,8 @@
76 // Callback for whats_changed to map the callback into the sync_exchange
77 // doc_ids_to_return array.
78 static int
79-whats_changed_to_doc_ids(void *context, const char *doc_id, int gen)
80+whats_changed_to_doc_ids(void *context, const char *doc_id, int gen,
81+ const char *trans_id)
82 {
83 struct lh_entry *e;
84 struct _whats_changed_doc_ids_state *state;
85
86=== modified file 'u1db/__init__.py'
87--- u1db/__init__.py 2012-05-18 09:33:19 +0000
88+++ u1db/__init__.py 2012-05-18 09:33:19 +0000
89@@ -57,11 +57,12 @@
90
91 :param old_generation: The generation of the database in the old
92 state.
93- :return: (cur_generation, [(doc_id, generation),...])
94+ :return: (cur_generation, [(doc_id, generation, trans_id),...])
95 The current generation of the database, and a list of of
96 changed documents since old_generation, represented by tuples
97- with for each document its doc_id and the generation corresponding
98- to the last intervening change and sorted by generation
99+ with for each document its doc_id and the generation and
100+ transaction id corresponding to the last intervening change and
101+ sorted by generation (old changes first)
102 """
103 raise NotImplementedError(self.whats_changed)
104
105
106=== modified file 'u1db/backends/inmemory.py'
107--- u1db/backends/inmemory.py 2012-05-18 09:33:19 +0000
108+++ u1db/backends/inmemory.py 2012-05-18 09:33:19 +0000
109@@ -207,7 +207,7 @@
110 generation = cur_generation
111 for doc_id, trans_id in reversed(relevant_tail):
112 if doc_id not in seen:
113- changes.append((doc_id, generation))
114+ changes.append((doc_id, generation, trans_id))
115 seen.add(doc_id)
116 generation -= 1
117 changes.reverse()
118
119=== modified file 'u1db/backends/sqlite_backend.py'
120--- u1db/backends/sqlite_backend.py 2012-05-18 09:33:19 +0000
121+++ u1db/backends/sqlite_backend.py 2012-05-18 09:33:19 +0000
122@@ -355,16 +355,17 @@
123
124 def whats_changed(self, old_generation=0):
125 c = self._db_handle.cursor()
126- c.execute("SELECT generation, doc_id FROM transaction_log"
127+ c.execute("SELECT generation, doc_id, transaction_id"
128+ " FROM transaction_log"
129 " WHERE generation > ? ORDER BY generation DESC",
130 (old_generation,))
131 results = c.fetchall()
132 cur_gen = old_generation
133 seen = set()
134 changes = []
135- for generation, doc_id in results:
136+ for generation, doc_id, trans_id in results:
137 if doc_id not in seen:
138- changes.append((doc_id, generation))
139+ changes.append((doc_id, generation, trans_id))
140 seen.add(doc_id)
141 if changes:
142 cur_gen = changes[0][1] # max generation
143
144=== modified file 'u1db/sync.py'
145--- u1db/sync.py 2012-05-18 09:33:19 +0000
146+++ u1db/sync.py 2012-05-18 09:33:19 +0000
147@@ -103,11 +103,11 @@
148 self.target_replica_uid)
149 if not changes and target_last_known_gen == target_gen:
150 return my_gen
151- changed_doc_ids = [doc_id for doc_id, _ in changes]
152+ changed_doc_ids = [doc_id for doc_id, _, _ in changes]
153 # prepare to send all the changed docs
154 docs_to_send = self.source.get_docs(changed_doc_ids,
155 check_for_conflicts=False)
156- docs_by_generation = zip(docs_to_send, (gen for _, gen in changes))
157+ docs_by_generation = zip(docs_to_send, (gen for _, gen, _ in changes))
158
159 # exchange documents and try to insert the returned ones with
160 # the target, return target synced-up-to gen
161@@ -206,10 +206,10 @@
162 self.new_gen = gen
163 seen_ids = self.seen_ids
164 # changed docs that weren't superseded by or converged with
165- self.changes_to_return = [(doc_id, gen) for (doc_id, gen) in changes
166- if doc_id not in seen_ids or
167- # there was a subsequent update
168- seen_ids.get(doc_id) < gen]
169+ self.changes_to_return = [(doc_id, gen) for (doc_id, gen, _) in changes
170+ # there was a subsequent update
171+ if doc_id not in seen_ids or
172+ seen_ids.get(doc_id) < gen]
173 return self.new_gen
174
175 def return_docs(self, return_doc_cb):
176
177=== modified file 'u1db/tests/__init__.py'
178--- u1db/tests/__init__.py 2012-05-17 15:13:42 +0000
179+++ u1db/tests/__init__.py 2012-05-18 09:33:19 +0000
180@@ -177,6 +177,10 @@
181 seen_transactions.add(transaction_id)
182 self.assertEqual(doc_ids, just_ids)
183
184+ def getLastTransId(self, db):
185+ """Return the transaction id for the last database update."""
186+ return self.db._get_transaction_log()[-1][-1]
187+
188
189 class ServerStateForTests(server_state.ServerState):
190 """Used in the test suite, so we don't have to touch disk, etc."""
191
192=== modified file 'u1db/tests/c_backend_wrapper.pyx'
193--- u1db/tests/c_backend_wrapper.pyx 2012-05-18 09:33:19 +0000
194+++ u1db/tests/c_backend_wrapper.pyx 2012-05-18 09:33:19 +0000
195@@ -61,8 +61,6 @@
196 ctypedef int (*u1db_key_callback)(void *context, char *key)
197 ctypedef int (*u1db_doc_gen_callback)(void *context,
198 u1db_document *doc, int gen)
199- ctypedef int (*u1db_doc_id_gen_callback)(void *context,
200- const_char_ptr doc_id, int gen)
201 ctypedef int (*u1db_trans_info_callback)(void *context,
202 const_char_ptr doc_id, int gen, const_char_ptr trans_id)
203
204@@ -85,7 +83,7 @@
205 int n_revs, const_char_ptr *revs)
206 int u1db_delete_doc(u1database *db, u1db_document *doc)
207 int u1db_whats_changed(u1database *db, int *gen, void *context,
208- u1db_doc_id_gen_callback cb)
209+ u1db_trans_info_callback cb)
210 int u1db__get_transaction_log(u1database *db, void *context,
211 u1db_trans_info_callback cb)
212 int u1db_get_doc_conflicts(u1database *db, char *doc_id, void *context,
213@@ -254,14 +252,6 @@
214 from sqlite3 import dbapi2
215
216
217-cdef int _append_doc_gen_to_list(void *context, const_char_ptr doc_id,
218- int generation) with gil:
219- a_list = <object>(context)
220- doc = doc_id
221- a_list.append((doc, generation))
222- return 0
223-
224-
225 cdef int _append_trans_info_to_list(void *context, const_char_ptr doc_id,
226 int generation,
227 const_char_ptr trans_id) with gil:
228@@ -958,7 +948,7 @@
229 c_generation = generation
230 handle_status("whats_changed",
231 u1db_whats_changed(self._db, &c_generation, <void*>a_list,
232- _append_doc_gen_to_list))
233+ _append_trans_info_to_list))
234 return c_generation, a_list
235
236 def _get_transaction_log(self):
237
238=== modified file 'u1db/tests/test_backends.py'
239--- u1db/tests/test_backends.py 2012-05-18 09:33:19 +0000
240+++ u1db/tests/test_backends.py 2012-05-18 09:33:19 +0000
241@@ -330,13 +330,17 @@
242 doc.content = '{"something": "else"}'
243 self.db.put_doc(doc)
244 self.assertTransactionLog([doc.doc_id, doc.doc_id], self.db)
245- self.assertEqual((2, [(doc.doc_id, 2)]), self.db.whats_changed())
246+ last_trans_id = self.getLastTransId(self.db)
247+ self.assertEqual((2, [(doc.doc_id, 2, last_trans_id)]),
248+ self.db.whats_changed())
249
250 def test_delete_updates_transaction_log(self):
251 doc = self.db.create_doc(simple_doc)
252 db_gen, _ = self.db.whats_changed()
253 self.db.delete_doc(doc)
254- self.assertEqual((2, [(doc.doc_id, 2)]), self.db.whats_changed(db_gen))
255+ last_trans_id = self.getLastTransId(self.db)
256+ self.assertEqual((2, [(doc.doc_id, 2, last_trans_id)]),
257+ self.db.whats_changed(db_gen))
258
259 def test_whats_changed_initial_database(self):
260 self.assertEqual((0, []), self.db.whats_changed())
261@@ -345,7 +349,9 @@
262 doc = self.db.create_doc(simple_doc)
263 doc.content = '{"new": "contents"}'
264 self.db.put_doc(doc)
265- self.assertEqual((2, [(doc.doc_id, 2)]), self.db.whats_changed())
266+ last_trans_id = self.getLastTransId(self.db)
267+ self.assertEqual((2, [(doc.doc_id, 2, last_trans_id)]),
268+ self.db.whats_changed())
269 self.assertEqual((2, []), self.db.whats_changed(2))
270
271 def test_whats_changed_returns_last_edits_ascending(self):
272@@ -353,15 +359,19 @@
273 doc1 = self.db.create_doc(simple_doc)
274 doc.content = '{"new": "contents"}'
275 self.db.delete_doc(doc1)
276+ delete_trans_id = self.getLastTransId(self.db)
277 self.db.put_doc(doc)
278- self.assertEqual((4, [(doc1.doc_id, 3), (doc.doc_id, 4)]),
279+ put_trans_id = self.getLastTransId(self.db)
280+ self.assertEqual((4, [(doc1.doc_id, 3, delete_trans_id),
281+ (doc.doc_id, 4, put_trans_id)]),
282 self.db.whats_changed())
283
284- def test_whats_changed_doesnt_includ_old_gen(self):
285+ def test_whats_changed_doesnt_include_old_gen(self):
286 self.db.create_doc(simple_doc)
287 self.db.create_doc(simple_doc)
288 doc2 = self.db.create_doc(simple_doc)
289- self.assertEqual((3, [(doc2.doc_id, 3)]),
290+ last_trans_id = self.getLastTransId(self.db)
291+ self.assertEqual((3, [(doc2.doc_id, 3, last_trans_id)]),
292 self.db.whats_changed(2))
293
294

Subscribers

People subscribed via source and target branches