Merge lp:~pedronis/u1db/open-vs-ensure into lp:u1db
- open-vs-ensure
- Merge into trunk
Proposed by
Samuele Pedroni
Status: | Superseded |
---|---|
Proposed branch: | lp:~pedronis/u1db/open-vs-ensure |
Merge into: | lp:u1db |
Diff against target: |
324 lines (+139/-28) 7 files modified
u1db/backends/sqlite_backend.py (+40/-10) u1db/commandline/client.py (+6/-5) u1db/errors.py (+4/-0) u1db/remote/server_state.py (+7/-0) u1db/tests/commandline/test_client.py (+5/-5) u1db/tests/test_server_state.py (+11/-0) u1db/tests/test_sqlite_backend.py (+66/-8) |
To merge this branch: | bzr merge lp:~pedronis/u1db/open-vs-ensure |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu One hackers | Pending | ||
Review via email: mp+83134@code.launchpad.net |
Commit message
Description of the change
Try to in down the workings/semantics of open_database:
- fail with DatabasesDoesNo
- try to be robust in case the database is being opened while another process is setting it up
add an ensure_database to create a database, expose it on server state (for use by later the http app)
To post a comment you must log in.
lp:~pedronis/u1db/open-vs-ensure
updated
- 126. By Samuele Pedroni
-
fold SQLiteDatabase.
ensure_ database into open_database( create= True) which is also the default - 127. By Samuele Pedroni
-
use open_database(
create= True) in test_client
Unmerged revisions
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'u1db/backends/sqlite_backend.py' |
2 | --- u1db/backends/sqlite_backend.py 2011-11-22 15:08:58 +0000 |
3 | +++ u1db/backends/sqlite_backend.py 2011-11-23 12:42:23 +0000 |
4 | @@ -14,8 +14,10 @@ |
5 | |
6 | """A U1DB implementation that uses SQLite as its persistence layer.""" |
7 | |
8 | +import os |
9 | import simplejson |
10 | from sqlite3 import dbapi2 |
11 | +import time |
12 | |
13 | from u1db.backends import CommonBackend, CommonSyncTarget |
14 | from u1db import Document, errors |
15 | @@ -35,18 +37,46 @@ |
16 | def get_sync_target(self): |
17 | return SQLiteSyncTarget(self) |
18 | |
19 | - @staticmethod |
20 | - def open_database(sqlite_file): |
21 | - # TODO: We really want a way to indicate that the database must already |
22 | - # exist. |
23 | + @classmethod |
24 | + def _which_index_storage(cls, c): |
25 | + try: |
26 | + c.execute("SELECT value FROM u1db_config" |
27 | + " WHERE name = 'index_storage'") |
28 | + except dbapi2.OperationalError, e: |
29 | + # The table does not exist yet |
30 | + return None, e |
31 | + else: |
32 | + return c.fetchone()[0], None |
33 | + |
34 | + @classmethod |
35 | + def _open_database(cls, sqlite_file): |
36 | + if not os.path.isfile(sqlite_file): |
37 | + raise errors.DatabaseDoesNotExist() |
38 | + tries = 2 |
39 | db_handle = dbapi2.connect(sqlite_file) |
40 | c = db_handle.cursor() |
41 | - c.execute("SELECT value FROM u1db_config WHERE name = 'index_storage'") |
42 | - v = c.fetchone() |
43 | - # if v is None: |
44 | - # raise ValueError('No defined index_storage for database %s' |
45 | - # % (sqlite_file,)) |
46 | - return SQLiteDatabase._sqlite_registry[v[0]](sqlite_file) |
47 | + while tries: |
48 | + v, err = cls._which_index_storage(c) |
49 | + if v is not None: |
50 | + break |
51 | + # possibly another process is initializing it, wait for it to be |
52 | + # done |
53 | + tries -= 1 |
54 | + time.sleep(0.5) |
55 | + else: |
56 | + raise err # go for the richest error? |
57 | + return SQLiteDatabase._sqlite_registry[v](sqlite_file) |
58 | + |
59 | + @classmethod |
60 | + def open_database(cls, sqlite_file, backend_cls=None, create=True): |
61 | + try: |
62 | + return cls._open_database(sqlite_file) |
63 | + except errors.DatabaseDoesNotExist: |
64 | + if not create: |
65 | + raise |
66 | + if backend_cls is None: |
67 | + backend_cls = SQLitePartialExpandDatabase # default |
68 | + return backend_cls(sqlite_file) |
69 | |
70 | @staticmethod |
71 | def register_implementation(klass): |
72 | |
73 | === modified file 'u1db/commandline/client.py' |
74 | --- u1db/commandline/client.py 2011-11-21 14:31:33 +0000 |
75 | +++ u1db/commandline/client.py 2011-11-23 12:42:23 +0000 |
76 | @@ -48,7 +48,7 @@ |
77 | def run(self, database, infile, doc_id): |
78 | if infile is None: |
79 | infile = self.stdin |
80 | - db = sqlite_backend.SQLiteDatabase.open_database(database) |
81 | + db = sqlite_backend.SQLiteDatabase.open_database(database, create=False) |
82 | doc = db.create_doc(infile.read(), doc_id=doc_id) |
83 | self.stderr.write('id: %s\nrev: %s\n' % (doc.doc_id, doc.rev)) |
84 | |
85 | @@ -71,7 +71,7 @@ |
86 | def run(self, database, doc_id, outfile): |
87 | if outfile is None: |
88 | outfile = self.stdout |
89 | - db = sqlite_backend.SQLiteDatabase.open_database(database) |
90 | + db = sqlite_backend.SQLiteDatabase.open_database(database, create=False) |
91 | doc = db.get_doc(doc_id) |
92 | outfile.write(doc.content) |
93 | self.stderr.write('rev: %s\n' % (doc.rev,)) |
94 | @@ -95,7 +95,7 @@ |
95 | help='The unique identifier for this database') |
96 | |
97 | def run(self, database, replica_uid): |
98 | - db = sqlite_backend.SQLitePartialExpandDatabase(database) |
99 | + db = sqlite_backend.SQLiteDatabase.open_database(database) |
100 | db._set_replica_uid(replica_uid) |
101 | |
102 | client_commands.register(CmdInitDB) |
103 | @@ -119,7 +119,7 @@ |
104 | def run(self, database, doc_id, doc_rev, infile): |
105 | if infile is None: |
106 | infile = self.stdin |
107 | - db = sqlite_backend.SQLiteDatabase.open_database(database) |
108 | + db = sqlite_backend.SQLiteDatabase.open_database(database, create=False) |
109 | doc = Document(doc_id, doc_rev, infile.read()) |
110 | doc_rev = db.put_doc(doc) |
111 | self.stderr.write('rev: %s\n' % (doc_rev,)) |
112 | @@ -147,7 +147,8 @@ |
113 | |
114 | def run(self, source, target): |
115 | """Start a Sync request.""" |
116 | - source_db = sqlite_backend.SQLiteDatabase.open_database(source) |
117 | + source_db = sqlite_backend.SQLiteDatabase.open_database(source, |
118 | + create=False) |
119 | st = self._open_target(target) |
120 | syncer = sync.Synchronizer(source_db, st) |
121 | syncer.sync() |
122 | |
123 | === modified file 'u1db/errors.py' |
124 | --- u1db/errors.py 2011-11-23 09:32:06 +0000 |
125 | +++ u1db/errors.py 2011-11-23 12:42:23 +0000 |
126 | @@ -37,3 +37,7 @@ |
127 | Can also be raised if wildcard matches are not strictly at the tail of the |
128 | request. |
129 | """ |
130 | + |
131 | + |
132 | +class DatabaseDoesNotExist(U1DBError): |
133 | + """The database does not exist.""" |
134 | |
135 | === modified file 'u1db/remote/server_state.py' |
136 | --- u1db/remote/server_state.py 2011-11-22 11:09:00 +0000 |
137 | +++ u1db/remote/server_state.py 2011-11-23 12:42:23 +0000 |
138 | @@ -42,4 +42,11 @@ |
139 | """Open a database at the given location.""" |
140 | from u1db.backends import sqlite_backend |
141 | full_path = self._relpath(path) |
142 | + return sqlite_backend.SQLiteDatabase.open_database(full_path, |
143 | + create=False) |
144 | + |
145 | + def ensure_database(self, path): |
146 | + """Ensure database at the given location.""" |
147 | + from u1db.backends import sqlite_backend |
148 | + full_path = self._relpath(path) |
149 | return sqlite_backend.SQLiteDatabase.open_database(full_path) |
150 | |
151 | === modified file 'u1db/tests/commandline/test_client.py' |
152 | --- u1db/tests/commandline/test_client.py 2011-11-23 09:34:59 +0000 |
153 | +++ u1db/tests/commandline/test_client.py 2011-11-23 12:42:23 +0000 |
154 | @@ -99,7 +99,7 @@ |
155 | super(TestCaseWithDB, self).setUp() |
156 | self.working_dir = self.createTempDir() |
157 | self.db_path = self.working_dir + '/test.db' |
158 | - self.db = sqlite_backend.SQLitePartialExpandDatabase(self.db_path) |
159 | + self.db = sqlite_backend.SQLiteDatabase.open_database(self.db_path) |
160 | self.db._set_replica_uid('test') |
161 | self.addCleanup(self.db.close) |
162 | |
163 | @@ -146,7 +146,7 @@ |
164 | cmd = self.make_command(client.CmdInitDB) |
165 | cmd.run(path, 'test-uid') |
166 | self.assertTrue(os.path.exists(path)) |
167 | - db = sqlite_backend.SQLiteDatabase.open_database(path) |
168 | + db = sqlite_backend.SQLiteDatabase.open_database(path, create=False) |
169 | self.assertEqual('test-uid', db._replica_uid) |
170 | |
171 | |
172 | @@ -174,7 +174,7 @@ |
173 | def setUp(self): |
174 | super(TestCmdSync, self).setUp() |
175 | self.db2_path = self.working_dir + '/test2.db' |
176 | - self.db2 = sqlite_backend.SQLitePartialExpandDatabase(self.db2_path) |
177 | + self.db2 = sqlite_backend.SQLiteDatabase.open_database(self.db2_path) |
178 | self.addCleanup(self.db2.close) |
179 | self.db2._set_replica_uid('test2') |
180 | self.doc = self.db.create_doc(tests.simple_doc, doc_id='test-id') |
181 | @@ -262,7 +262,7 @@ |
182 | def test_init_db(self): |
183 | path = self.working_dir + '/test2.db' |
184 | ret, stdout, stderr = self.run_main(['init-db', path, 'uid']) |
185 | - db2 = sqlite_backend.SQLiteDatabase.open_database(path) |
186 | + db2 = sqlite_backend.SQLiteDatabase.open_database(path, create=False) |
187 | |
188 | def test_put(self): |
189 | doc = self.db.create_doc(tests.simple_doc, doc_id='test-id') |
190 | @@ -279,7 +279,7 @@ |
191 | def test_sync(self): |
192 | doc = self.db.create_doc(tests.simple_doc, doc_id='test-id') |
193 | self.db2_path = self.working_dir + '/test2.db' |
194 | - self.db2 = sqlite_backend.SQLitePartialExpandDatabase(self.db2_path) |
195 | + self.db2 = sqlite_backend.SQLiteDatabase.open_database(self.db2_path) |
196 | self.addCleanup(self.db2.close) |
197 | ret, stdout, stderr = self.run_main( |
198 | ['sync', self.db_path, self.db2_path]) |
199 | |
200 | === modified file 'u1db/tests/test_server_state.py' |
201 | --- u1db/tests/test_server_state.py 2011-11-23 09:31:16 +0000 |
202 | +++ u1db/tests/test_server_state.py 2011-11-23 12:42:23 +0000 |
203 | @@ -46,3 +46,14 @@ |
204 | sqlite_backend.SQLitePartialExpandDatabase(path) |
205 | db = self.state.open_database('test.db') |
206 | self.assertIsInstance(db, sqlite_backend.SQLitePartialExpandDatabase) |
207 | + |
208 | + def test_ensure_database(self): |
209 | + tempdir = self.createTempDir() |
210 | + self.state.set_workingdir(tempdir) |
211 | + path = tempdir + '/test.db' |
212 | + self.assertFalse(os.path.exists(path)) |
213 | + db = self.state.ensure_database('test.db') |
214 | + self.assertIsInstance(db, sqlite_backend.SQLitePartialExpandDatabase) |
215 | + self.assertTrue(os.path.exists(path)) |
216 | + db2 = self.state.open_database('test.db') |
217 | + self.assertIsInstance(db2, sqlite_backend.SQLitePartialExpandDatabase) |
218 | |
219 | === modified file 'u1db/tests/test_sqlite_backend.py' |
220 | --- u1db/tests/test_sqlite_backend.py 2011-11-23 09:34:59 +0000 |
221 | +++ u1db/tests/test_sqlite_backend.py 2011-11-23 12:42:23 +0000 |
222 | @@ -15,14 +15,13 @@ |
223 | """Test sqlite backend internals.""" |
224 | |
225 | import os |
226 | -import tempfile |
227 | import time |
228 | import threading |
229 | -import shutil |
230 | |
231 | from sqlite3 import dbapi2 |
232 | |
233 | from u1db import ( |
234 | + errors, |
235 | tests, |
236 | ) |
237 | from u1db.backends import sqlite_backend |
238 | @@ -72,7 +71,7 @@ |
239 | db1 = SQLiteDatabaseTesting(dbname, 1) |
240 | t2.join() |
241 | |
242 | - self.assertTrue(isinstance(outcome2[0], SQLiteDatabaseTesting)) |
243 | + self.assertIsInstance(outcome2[0], SQLiteDatabaseTesting) |
244 | db2 = outcome2[0] |
245 | self.assertTrue(db2._is_initialized(db1._get_sqlite_handle().cursor())) |
246 | |
247 | @@ -210,13 +209,72 @@ |
248 | (doc1.doc_id, "sub.doc", "underneath"), |
249 | ], c.fetchall()) |
250 | |
251 | - def test_open_database(self): |
252 | - temp_dir = tempfile.mkdtemp(prefix='u1db-test-') |
253 | - self.addCleanup(shutil.rmtree, temp_dir) |
254 | + def test__open_database(self): |
255 | + temp_dir = self.createTempDir(prefix='u1db-test-') |
256 | path = temp_dir + '/test.sqlite' |
257 | db = sqlite_backend.SQLitePartialExpandDatabase(path) |
258 | - db2 = sqlite_backend.SQLiteDatabase.open_database(path) |
259 | - self.assertIsInstance(db2, sqlite_backend.SQLitePartialExpandDatabase) |
260 | + db2 = sqlite_backend.SQLiteDatabase._open_database(path) |
261 | + self.assertIsInstance(db2, sqlite_backend.SQLitePartialExpandDatabase) |
262 | + |
263 | + def test__open_database_non_existent(self): |
264 | + temp_dir = self.createTempDir(prefix='u1db-test-') |
265 | + path = temp_dir + '/non-existent.sqlite' |
266 | + self.assertRaises(errors.DatabaseDoesNotExist, |
267 | + sqlite_backend.SQLiteDatabase._open_database, path) |
268 | + |
269 | + def test__open_database_during_init(self): |
270 | + temp_dir = self.createTempDir(prefix='u1db-test-') |
271 | + path = temp_dir + '/initialised.db' |
272 | + db = sqlite_backend.SQLitePartialExpandDatabase.__new__( |
273 | + sqlite_backend.SQLitePartialExpandDatabase) |
274 | + db._db_handle = dbapi2.connect(path) # db is there but not yet init-ed |
275 | + observed = [] |
276 | + class SQLiteDatabaseTesting(sqlite_backend.SQLiteDatabase): |
277 | + @classmethod |
278 | + def _which_index_storage(cls, c): |
279 | + res = super(SQLiteDatabaseTesting, cls)._which_index_storage(c) |
280 | + db._ensure_schema() # init db |
281 | + observed.append(res[0]) |
282 | + return res |
283 | + db2 = SQLiteDatabaseTesting._open_database(path) |
284 | + self.assertIsInstance(db2, sqlite_backend.SQLitePartialExpandDatabase) |
285 | + self.assertEqual([None, |
286 | + sqlite_backend.SQLitePartialExpandDatabase._index_storage_value], |
287 | + observed) |
288 | + |
289 | + def test__open_database_invalid(self): |
290 | + temp_dir = self.createTempDir(prefix='u1db-test-') |
291 | + path1 = temp_dir + '/invalid1.db' |
292 | + with open(path1, 'wb') as f: |
293 | + f.write("") |
294 | + self.assertRaises(dbapi2.OperationalError, |
295 | + sqlite_backend.SQLiteDatabase._open_database, path1) |
296 | + path2 = temp_dir + '/invalid2.db' |
297 | + with open(path1, 'wb') as f: |
298 | + f.write("invalid") |
299 | + self.assertRaises(dbapi2.DatabaseError, |
300 | + sqlite_backend.SQLiteDatabase._open_database, path1) |
301 | + |
302 | + def test_open_database_existing(self): |
303 | + temp_dir = self.createTempDir(prefix='u1db-test-') |
304 | + path = temp_dir + '/existing.sqlite' |
305 | + db = sqlite_backend.SQLitePartialExpandDatabase(path) |
306 | + db2 = sqlite_backend.SQLiteDatabase.open_database(path, create=False) |
307 | + self.assertIsInstance(db2, sqlite_backend.SQLitePartialExpandDatabase) |
308 | + |
309 | + def test_open_database_create(self): |
310 | + temp_dir = self.createTempDir(prefix='u1db-test-') |
311 | + path = temp_dir + '/new.sqlite' |
312 | + db = sqlite_backend.SQLiteDatabase.open_database(path) |
313 | + db2 = sqlite_backend.SQLiteDatabase.open_database(path, create=False) |
314 | + self.assertIsInstance(db2, sqlite_backend.SQLitePartialExpandDatabase) |
315 | + |
316 | + def test_open_database_non_existent(self): |
317 | + temp_dir = self.createTempDir(prefix='u1db-test-') |
318 | + path = temp_dir + '/non-existent.sqlite' |
319 | + self.assertRaises(errors.DatabaseDoesNotExist, |
320 | + sqlite_backend.SQLiteDatabase.open_database, path, |
321 | + create=False) |
322 | |
323 | def assertTransform(self, sql_value, value): |
324 | transformed = sqlite_backend.SQLiteDatabase._transform_glob(value) |