def test_next_only_one_server(self): from relstorage.adapters.replica import ReplicaSelector with open(self.fn, 'w') as f: f.write('localhost\n') rs = ReplicaSelector(self.fn, 600.0) self.assertEqual(rs.current(), 'localhost') self.assertEqual(rs.next(), None)
def test_next_only_one_server(self): from relstorage.adapters.replica import ReplicaSelector f = open(self.fn, 'w') f.write('localhost\n') f.close() rs = ReplicaSelector(self.fn, 600.0) self.assertEqual(rs.current(), 'localhost') self.assertEqual(rs.next(), None)
def test_next_only_one_server(self): from relstorage.adapters.replica import ReplicaSelector f = open(self.fn, 'w') f.write('localhost\n') f.close() rs = ReplicaSelector(self.options) self.assertEqual(rs.current(), 'localhost') self.assertEqual(rs.next(), None)
def test__select(self): from relstorage.adapters.replica import ReplicaSelector rs = ReplicaSelector(self.fn, 600.0) rs._select(0) self.assertEqual(rs._current_replica, 'example.com:1234') self.assertEqual(rs._current_index, 0) self.assertEqual(rs._expiration, None) rs._select(1) self.assertEqual(rs._current_replica, 'localhost:4321') self.assertEqual(rs._current_index, 1) self.assertNotEqual(rs._expiration, None)
def __init__(self, options): # options is a relstorage.options.Options instance if options.replica_conf: self.replica_selector = ReplicaSelector(options.replica_conf, options.replica_timeout) else: self.replica_selector = None if options.ro_replica_conf: self.ro_replica_selector = ReplicaSelector(options.ro_replica_conf, options.replica_timeout) else: self.ro_replica_selector = self.replica_selector
def test_current(self): from relstorage.adapters.replica import ReplicaSelector rs = ReplicaSelector(self.fn, 600.0) self.assertEqual(rs.current(), 'example.com:1234') # change the file and get the new current replica f = open(self.fn, 'w') f.write('localhost\nalternate\n') f.close() rs._config_checked = 0 rs._config_modified = 0 self.assertEqual(rs.current(), 'localhost') # switch to the alternate rs._select(1) self.assertEqual(rs.current(), 'alternate') # expire the alternate rs._expiration = 0 self.assertEqual(rs.current(), 'localhost')
def test_next_iteration(self): from relstorage.adapters.replica import ReplicaSelector rs = ReplicaSelector(self.fn, 600.0) # test forward iteration self.assertEqual(rs.current(), 'example.com:1234') self.assertEqual(rs.next(), 'localhost:4321') self.assertEqual(rs.next(), 'localhost:9999') self.assertEqual(rs.next(), None) # test iteration that skips over the replica that failed self.assertEqual(rs.current(), 'example.com:1234') self.assertEqual(rs.next(), 'localhost:4321') self.assertEqual(rs.current(), 'localhost:4321') # next() after current() indicates the last replica failed self.assertEqual(rs.next(), 'example.com:1234') self.assertEqual(rs.next(), 'localhost:9999') self.assertEqual(rs.next(), None)
def test__is_config_modified(self): from relstorage.adapters.replica import ReplicaSelector import time rs = ReplicaSelector(self.fn, 600.0) self.assertEqual(rs._is_config_modified(), False) # change the file rs._config_modified = 0 # don't check the file yet rs._config_checked = time.time() + 3600 self.assertEqual(rs._is_config_modified(), False) # now check the file rs._config_checked = 0 self.assertEqual(rs._is_config_modified(), True)
def test_next_with_new_conf(self): from relstorage.adapters.replica import ReplicaSelector rs = ReplicaSelector(self.fn, 600.0) self.assertEqual(rs.current(), 'example.com:1234') self.assertEqual(rs.next(), 'localhost:4321') # interrupt the iteration by changing the replica conf file with open(self.fn, 'w') as f: f.write('example.com:9999\n') rs._config_checked = 0 rs._config_modified = 0 self.assertEqual(rs.next(), 'example.com:9999') self.assertEqual(rs.next(), None)
def test_next_with_new_conf(self): from relstorage.adapters.replica import ReplicaSelector rs = ReplicaSelector(self.fn, 600.0) self.assertEqual(rs.current(), 'example.com:1234') self.assertEqual(rs.next(), 'localhost:4321') # interrupt the iteration by changing the replica conf file f = open(self.fn, 'w') f.write('example.com:9999\n') f.close() rs._config_checked = 0 rs._config_modified = 0 self.assertEqual(rs.next(), 'example.com:9999') self.assertEqual(rs.next(), None)
def test__read_config_normal(self): from relstorage.adapters.replica import ReplicaSelector rs = ReplicaSelector(self.fn, 600.0) self.assertEqual( rs._replicas, ['example.com:1234', 'localhost:4321', 'localhost:9999'])
class AbstractConnectionManager(object): """Abstract base class for connection management. Responsible for opening and closing database connections. """ implements(IConnectionManager) # disconnected_exceptions contains the exception types that might be # raised when the connection to the database has been broken. disconnected_exceptions = (ReplicaClosedException,) # close_exceptions contains the exception types to ignore # when the adapter attempts to close a database connection. close_exceptions = () # on_store_opened is either None or a callable that # will be called whenever a store cursor is opened or rolled back. on_store_opened = None def __init__(self, options): # options is a relstorage.options.Options instance if options.replica_conf: self.replica_selector = ReplicaSelector(options) else: self.replica_selector = None def set_on_store_opened(self, f): """Set the on_store_opened hook""" self.on_store_opened = f def open(self): """Open a database connection and return (conn, cursor).""" raise NotImplementedError() def close(self, conn, cursor): """Close a connection and cursor, ignoring certain errors. """ for obj in (cursor, conn): if obj is not None: try: obj.close() except self.close_exceptions: pass def open_and_call(self, callback): """Call a function with an open connection and cursor. If the function returns, commits the transaction and returns the result returned by the function. If the function raises an exception, aborts the transaction then propagates the exception. """ conn, cursor = self.open() try: try: res = callback(conn, cursor) except: conn.rollback() raise else: conn.commit() return res finally: self.close(conn, cursor) def open_for_load(self): raise NotImplementedError() def restart_load(self, conn, cursor): """Reinitialize a connection for loading objects.""" self.check_replica(conn, cursor) conn.rollback() def check_replica(self, conn, cursor): """Raise an exception if the connection belongs to an old replica""" if self.replica_selector is not None: current = self.replica_selector.current() if conn.replica != current: # Prompt the change to a new replica by raising an exception. self.close(conn, cursor) raise ReplicaClosedException( "Switched replica from %s to %s" % (conn.replica, current)) def open_for_store(self): """Open and initialize a connection for storing objects. Returns (conn, cursor). """ conn, cursor = self.open() try: if self.on_store_opened is not None: self.on_store_opened(cursor, restart=False) return conn, cursor except: self.close(conn, cursor) raise def restart_store(self, conn, cursor): """Reuse a store connection.""" self.check_replica(conn, cursor) conn.rollback() if self.on_store_opened is not None: self.on_store_opened(cursor, restart=True) def open_for_pre_pack(self): """Open a connection to be used for the pre-pack phase. Returns (conn, cursor). """ return self.open()