Skip to content

Commit

Permalink
Handle DB creation race condition
Browse files Browse the repository at this point in the history
Till now if a race occurs on a request to a DB when it is
first created, the loosing party die with 500...
... and an error stating a race is in the log file.

Both removed by simply accepting the fact that the DB is in fact
created by someone else - just less work for us to do... ;)

Fixes Bug #1051998

Change-Id: I8e29bf362edf2b1a84f9bffa39c04d5b3dc241b2
  • Loading branch information
davidhIBM authored and openstack-gerrit committed May 24, 2013
1 parent 43bf568 commit 9745a28
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 12 deletions.
14 changes: 10 additions & 4 deletions swift/account/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,11 @@ def PUT(self, req):
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)
Expand All @@ -130,8 +133,11 @@ def PUT(self, req):
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:
Expand Down
14 changes: 11 additions & 3 deletions swift/common/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ def __str__(self):
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."""

Expand Down Expand Up @@ -247,9 +257,7 @@ def initialize(self, put_timestamp=None):
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:
Expand Down
19 changes: 14 additions & 5 deletions swift/container/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,11 @@ def DELETE(self, req):
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
Expand Down Expand Up @@ -247,7 +250,10 @@ def PUT(self, req):
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']),
Expand All @@ -256,8 +262,11 @@ def PUT(self, req):
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)
Expand Down
11 changes: 11 additions & 0 deletions test/unit/common/test_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import simplejson
import sqlite3
from mock import patch

import swift.common.db
from swift.common.db import AccountBroker, chexor, ContainerBroker, \
Expand Down Expand Up @@ -99,6 +100,7 @@ def setUp(self):
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)
Expand Down Expand Up @@ -147,6 +149,15 @@ def stub(*args, **kwargs):
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)')
Expand Down

0 comments on commit 9745a28

Please sign in to comment.