Merge "Handle DB creation race condition"

This commit is contained in:
Jenkins 2013-05-30 20:27:22 +00:00 committed by Gerrit Code Review
commit 5501a4031f
4 changed files with 46 additions and 12 deletions

View File

@ -113,8 +113,11 @@ class AccountController(object):
broker.pending_timeout = 3
if account.startswith(self.auto_create_account_prefix) and \
not os.path.exists(broker.db_file):
broker.initialize(normalize_timestamp(
req.headers.get('x-timestamp') or time.time()))
try:
broker.initialize(normalize_timestamp(
req.headers.get('x-timestamp') or time.time()))
except swift.common.db.DatabaseAlreadyExists:
pass
if req.headers.get('x-account-override-deleted', 'no').lower() != \
'yes' and broker.is_deleted():
return HTTPNotFound(request=req)
@ -130,8 +133,11 @@ class AccountController(object):
else: # put account
timestamp = normalize_timestamp(req.headers['x-timestamp'])
if not os.path.exists(broker.db_file):
broker.initialize(timestamp)
created = True
try:
broker.initialize(timestamp)
created = True
except swift.common.db.DatabaseAlreadyExists:
pass
elif broker.is_status_deleted():
return HTTPForbidden(request=req, body='Recently deleted')
else:

View File

@ -70,6 +70,16 @@ class DatabaseConnectionError(sqlite3.DatabaseError):
self.path, self.timeout, self.msg)
class DatabaseAlreadyExists(sqlite3.DatabaseError):
"""More friendly error messages for DB Errors."""
def __init__(self, path):
self.path = path
def __str__(self):
return 'DB %s already exists' % self.path
class GreenDBConnection(sqlite3.Connection):
"""SQLite DB Connection handler that plays well with eventlet."""
@ -247,9 +257,7 @@ class DatabaseBroker(object):
if os.path.exists(self.db_file):
# It's as if there was a "condition" where different parts
# of the system were "racing" each other.
raise DatabaseConnectionError(
self.db_file,
'DB created by someone else while working?')
raise DatabaseAlreadyExists(self.db_file)
renamer(tmp_db_file, self.db_file)
self.conn = get_db_connection(self.db_file, self.timeout)
else:

View File

@ -198,8 +198,11 @@ class ContainerController(object):
broker = self._get_container_broker(drive, part, account, container)
if account.startswith(self.auto_create_account_prefix) and obj and \
not os.path.exists(broker.db_file):
broker.initialize(normalize_timestamp(
req.headers.get('x-timestamp') or time.time()))
try:
broker.initialize(normalize_timestamp(
req.headers.get('x-timestamp') or time.time()))
except swift.common.db.DatabaseAlreadyExists:
pass
if not os.path.exists(broker.db_file):
return HTTPNotFound()
if obj: # delete object
@ -247,7 +250,10 @@ class ContainerController(object):
if obj: # put container object
if account.startswith(self.auto_create_account_prefix) and \
not os.path.exists(broker.db_file):
broker.initialize(timestamp)
try:
broker.initialize(timestamp)
except swift.common.db.DatabaseAlreadyExists:
pass
if not os.path.exists(broker.db_file):
return HTTPNotFound()
broker.put_object(obj, timestamp, int(req.headers['x-size']),
@ -256,8 +262,11 @@ class ContainerController(object):
return HTTPCreated(request=req)
else: # put container
if not os.path.exists(broker.db_file):
broker.initialize(timestamp)
created = True
try:
broker.initialize(timestamp)
created = True
except swift.common.db.DatabaseAlreadyExists:
pass
else:
created = broker.is_deleted()
broker.update_put_timestamp(timestamp)

View File

@ -25,6 +25,7 @@ from uuid import uuid4
import simplejson
import sqlite3
from mock import patch
import swift.common.db
from swift.common.db import AccountBroker, chexor, ContainerBroker, \
@ -99,6 +100,7 @@ class TestDatabaseBroker(unittest.TestCase):
def test_DB_PREALLOCATION_setting(self):
u = uuid4().hex
b = DatabaseBroker(u)
swift.common.db.DB_PREALLOCATION = False
b._preallocate()
swift.common.db.DB_PREALLOCATION = True
self.assertRaises(OSError, b._preallocate)
@ -147,6 +149,15 @@ class TestDatabaseBroker(unittest.TestCase):
conn.execute('SELECT * FROM outgoing_sync')
conn.execute('SELECT * FROM incoming_sync')
def my_exists(*a, **kw):
return True
with patch('os.path.exists', my_exists):
broker = DatabaseBroker(os.path.join(self.testdir, '1.db'))
broker._initialize = stub
self.assertRaises(swift.common.db.DatabaseAlreadyExists,
broker.initialize, normalize_timestamp('1'))
def test_delete_db(self):
def init_stub(conn, put_timestamp):
conn.execute('CREATE TABLE test (one TEXT)')