Merge "Handle DB creation race condition"
This commit is contained in:
commit
5501a4031f
@ -113,8 +113,11 @@ class AccountController(object):
|
|||||||
broker.pending_timeout = 3
|
broker.pending_timeout = 3
|
||||||
if account.startswith(self.auto_create_account_prefix) and \
|
if account.startswith(self.auto_create_account_prefix) and \
|
||||||
not os.path.exists(broker.db_file):
|
not os.path.exists(broker.db_file):
|
||||||
|
try:
|
||||||
broker.initialize(normalize_timestamp(
|
broker.initialize(normalize_timestamp(
|
||||||
req.headers.get('x-timestamp') or time.time()))
|
req.headers.get('x-timestamp') or time.time()))
|
||||||
|
except swift.common.db.DatabaseAlreadyExists:
|
||||||
|
pass
|
||||||
if req.headers.get('x-account-override-deleted', 'no').lower() != \
|
if req.headers.get('x-account-override-deleted', 'no').lower() != \
|
||||||
'yes' and broker.is_deleted():
|
'yes' and broker.is_deleted():
|
||||||
return HTTPNotFound(request=req)
|
return HTTPNotFound(request=req)
|
||||||
@ -130,8 +133,11 @@ class AccountController(object):
|
|||||||
else: # put account
|
else: # put account
|
||||||
timestamp = normalize_timestamp(req.headers['x-timestamp'])
|
timestamp = normalize_timestamp(req.headers['x-timestamp'])
|
||||||
if not os.path.exists(broker.db_file):
|
if not os.path.exists(broker.db_file):
|
||||||
|
try:
|
||||||
broker.initialize(timestamp)
|
broker.initialize(timestamp)
|
||||||
created = True
|
created = True
|
||||||
|
except swift.common.db.DatabaseAlreadyExists:
|
||||||
|
pass
|
||||||
elif broker.is_status_deleted():
|
elif broker.is_status_deleted():
|
||||||
return HTTPForbidden(request=req, body='Recently deleted')
|
return HTTPForbidden(request=req, body='Recently deleted')
|
||||||
else:
|
else:
|
||||||
|
@ -70,6 +70,16 @@ class DatabaseConnectionError(sqlite3.DatabaseError):
|
|||||||
self.path, self.timeout, self.msg)
|
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):
|
class GreenDBConnection(sqlite3.Connection):
|
||||||
"""SQLite DB Connection handler that plays well with eventlet."""
|
"""SQLite DB Connection handler that plays well with eventlet."""
|
||||||
|
|
||||||
@ -247,9 +257,7 @@ class DatabaseBroker(object):
|
|||||||
if os.path.exists(self.db_file):
|
if os.path.exists(self.db_file):
|
||||||
# It's as if there was a "condition" where different parts
|
# It's as if there was a "condition" where different parts
|
||||||
# of the system were "racing" each other.
|
# of the system were "racing" each other.
|
||||||
raise DatabaseConnectionError(
|
raise DatabaseAlreadyExists(self.db_file)
|
||||||
self.db_file,
|
|
||||||
'DB created by someone else while working?')
|
|
||||||
renamer(tmp_db_file, self.db_file)
|
renamer(tmp_db_file, self.db_file)
|
||||||
self.conn = get_db_connection(self.db_file, self.timeout)
|
self.conn = get_db_connection(self.db_file, self.timeout)
|
||||||
else:
|
else:
|
||||||
|
@ -198,8 +198,11 @@ class ContainerController(object):
|
|||||||
broker = self._get_container_broker(drive, part, account, container)
|
broker = self._get_container_broker(drive, part, account, container)
|
||||||
if account.startswith(self.auto_create_account_prefix) and obj and \
|
if account.startswith(self.auto_create_account_prefix) and obj and \
|
||||||
not os.path.exists(broker.db_file):
|
not os.path.exists(broker.db_file):
|
||||||
|
try:
|
||||||
broker.initialize(normalize_timestamp(
|
broker.initialize(normalize_timestamp(
|
||||||
req.headers.get('x-timestamp') or time.time()))
|
req.headers.get('x-timestamp') or time.time()))
|
||||||
|
except swift.common.db.DatabaseAlreadyExists:
|
||||||
|
pass
|
||||||
if not os.path.exists(broker.db_file):
|
if not os.path.exists(broker.db_file):
|
||||||
return HTTPNotFound()
|
return HTTPNotFound()
|
||||||
if obj: # delete object
|
if obj: # delete object
|
||||||
@ -247,7 +250,10 @@ class ContainerController(object):
|
|||||||
if obj: # put container object
|
if obj: # put container object
|
||||||
if account.startswith(self.auto_create_account_prefix) and \
|
if account.startswith(self.auto_create_account_prefix) and \
|
||||||
not os.path.exists(broker.db_file):
|
not os.path.exists(broker.db_file):
|
||||||
|
try:
|
||||||
broker.initialize(timestamp)
|
broker.initialize(timestamp)
|
||||||
|
except swift.common.db.DatabaseAlreadyExists:
|
||||||
|
pass
|
||||||
if not os.path.exists(broker.db_file):
|
if not os.path.exists(broker.db_file):
|
||||||
return HTTPNotFound()
|
return HTTPNotFound()
|
||||||
broker.put_object(obj, timestamp, int(req.headers['x-size']),
|
broker.put_object(obj, timestamp, int(req.headers['x-size']),
|
||||||
@ -256,8 +262,11 @@ class ContainerController(object):
|
|||||||
return HTTPCreated(request=req)
|
return HTTPCreated(request=req)
|
||||||
else: # put container
|
else: # put container
|
||||||
if not os.path.exists(broker.db_file):
|
if not os.path.exists(broker.db_file):
|
||||||
|
try:
|
||||||
broker.initialize(timestamp)
|
broker.initialize(timestamp)
|
||||||
created = True
|
created = True
|
||||||
|
except swift.common.db.DatabaseAlreadyExists:
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
created = broker.is_deleted()
|
created = broker.is_deleted()
|
||||||
broker.update_put_timestamp(timestamp)
|
broker.update_put_timestamp(timestamp)
|
||||||
|
@ -25,6 +25,7 @@ from uuid import uuid4
|
|||||||
|
|
||||||
import simplejson
|
import simplejson
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
from mock import patch
|
||||||
|
|
||||||
import swift.common.db
|
import swift.common.db
|
||||||
from swift.common.db import AccountBroker, chexor, ContainerBroker, \
|
from swift.common.db import AccountBroker, chexor, ContainerBroker, \
|
||||||
@ -99,6 +100,7 @@ class TestDatabaseBroker(unittest.TestCase):
|
|||||||
def test_DB_PREALLOCATION_setting(self):
|
def test_DB_PREALLOCATION_setting(self):
|
||||||
u = uuid4().hex
|
u = uuid4().hex
|
||||||
b = DatabaseBroker(u)
|
b = DatabaseBroker(u)
|
||||||
|
swift.common.db.DB_PREALLOCATION = False
|
||||||
b._preallocate()
|
b._preallocate()
|
||||||
swift.common.db.DB_PREALLOCATION = True
|
swift.common.db.DB_PREALLOCATION = True
|
||||||
self.assertRaises(OSError, b._preallocate)
|
self.assertRaises(OSError, b._preallocate)
|
||||||
@ -147,6 +149,15 @@ class TestDatabaseBroker(unittest.TestCase):
|
|||||||
conn.execute('SELECT * FROM outgoing_sync')
|
conn.execute('SELECT * FROM outgoing_sync')
|
||||||
conn.execute('SELECT * FROM incoming_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 test_delete_db(self):
|
||||||
def init_stub(conn, put_timestamp):
|
def init_stub(conn, put_timestamp):
|
||||||
conn.execute('CREATE TABLE test (one TEXT)')
|
conn.execute('CREATE TABLE test (one TEXT)')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user