diff options
Diffstat (limited to 'src/s3ql/metadata.py')
-rw-r--r-- | src/s3ql/metadata.py | 72 |
1 files changed, 57 insertions, 15 deletions
diff --git a/src/s3ql/metadata.py b/src/s3ql/metadata.py index cc0cd49..74b44dc 100644 --- a/src/s3ql/metadata.py +++ b/src/s3ql/metadata.py @@ -7,9 +7,11 @@ This program can be distributed under the terms of the GNU GPLv3. ''' from __future__ import division, print_function, absolute_import - -from .deltadump import (INTEGER, BLOB, TIME, dump_table, load_table) +from .deltadump import INTEGER, BLOB, TIME, dump_table, load_table +from .database import Connection import logging +import os +import stat log = logging.getLogger('metadata') @@ -61,18 +63,47 @@ DUMP_SPEC = [ -def restore_metadata(fh, db): - '''Read metadata from *fh* and write into *db* +def restore_metadata(fh, dbfile): + '''Read metadata from *fh* and write into *dbfile* + + Return database connection to *dbfile*. *fh* must be able to return an actual file descriptor from its `fileno` method. + + *dbfile* will be created with 0600 permissions. Data is + first written into a temporary file *dbfile* + '.tmp', and + the file is renamed once all data has been loaded. + + ''' - create_tables(db) - for (table, _, columns) in DUMP_SPEC: - log.info('..%s..', table) - load_table(table, columns, db=db, fh=fh) - db.execute('ANALYZE') + tmpfile = dbfile + '.tmp' + fd = os.open(tmpfile, os.O_RDWR | os.O_CREAT | os.O_TRUNC, + stat.S_IRUSR | stat.S_IWUSR) + try: + os.close(fd) + + db = Connection(tmpfile) + db.execute('PRAGMA locking_mode = NORMAL') + db.execute('PRAGMA synchronous = OFF') + db.execute('PRAGMA journal_mode = OFF') + create_tables(db) + + for (table, _, columns) in DUMP_SPEC: + log.info('..%s..', table) + load_table(table, columns, db=db, fh=fh) + db.execute('ANALYZE') + + # We must close the database to rename it + db.close() + except: + os.unlink(tmpfile) + raise + + os.rename(tmpfile, dbfile) + + return Connection(dbfile) def cycle_metadata(backend): from .backends.common import NoSuchObject @@ -80,11 +111,11 @@ def cycle_metadata(backend): log.info('Backing up old metadata...') for i in reversed(range(10)): try: - backend.copy("s3ql_metadata_bak_%d" % i, "s3ql_metadata_bak_%d" % (i + 1)) + backend.rename("s3ql_metadata_bak_%d" % i, "s3ql_metadata_bak_%d" % (i + 1)) except NoSuchObject: pass - backend.copy("s3ql_metadata", "s3ql_metadata_bak_0") + backend.rename("s3ql_metadata", "s3ql_metadata_bak_0") def dump_metadata(db, fh): '''Dump metadata into fh @@ -93,10 +124,21 @@ def dump_metadata(db, fh): its `fileno` method. ''' - for (table, order, columns) in DUMP_SPEC: - log.info('..%s..', table) - dump_table(table, order, columns, db=db, fh=fh) - + locking_mode = db.get_val('PRAGMA locking_mode') + try: + # Ensure that we don't hold a lock on the db + # (need to access DB to actually release locks) + db.execute('PRAGMA locking_mode = NORMAL') + db.has_val('SELECT rowid FROM %s LIMIT 1' % DUMP_SPEC[0][0]) + + for (table, order, columns) in DUMP_SPEC: + log.info('..%s..', table) + dump_table(table, order, columns, db=db, fh=fh) + + finally: + db.execute('PRAGMA locking_mode = %s' % locking_mode) + + def create_tables(conn): # Table of storage objects # Refcount is included for performance reasons |