summaryrefslogtreecommitdiff
path: root/src/s3ql/metadata.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/s3ql/metadata.py')
-rw-r--r--src/s3ql/metadata.py72
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