def __init__(self, site_alias: str, client_socket: socket.socket, **context): threading.Thread.__init__(self, daemon=True) communication.GenericSocketUser.__init__(self) self.protocol_db = protocol.ProtocolDatabase(context['protocol_db']) self.socket = client_socket self.context = context self.active_map = {} # Initialize our site-list, which describes our cluster. self.site_list = self.context['site_list'] logger.info(f'Coordinator is aware of site: {self.site_list}') # Identify which node we are in our "ring". self.node_id = [ i for i, j in enumerate(self.site_list) if j['alias'] == site_alias ][0] logger.info(f'Transaction started at node {self.node_id}.') # Setup a connection to the RM (i.e. Postgres). self.conn = psycopg2.connect( user=self.context['postgres_username'], password=self.context['postgres_password'], host=self.context['postgres_hostname'], database=self.context['postgres_database']) # To enter the ABORT / POLLING state instead, parent must explicitly change the state after instantiation. self.transaction_id = psycopg2.extensions.Xid.from_string( str(uuid.uuid4())) self.state = CoordinatorStates.INITIALIZE self.previous_state = None
def _recovery_state(self): # Initialize our site-list, which describes our cluster. logger.info(f"TM is aware of site: {self.site_list}") # Setup a connection to the RM (i.e. Postgres). recovery_conn = psycopg2.connect( user=self.context['postgres_username'], password=self.context['postgres_password'], host=self.context['postgres_hostname'], database=self.context['postgres_database'] ) if 'test_rm' not in self.context else self.context['test_rm'] protocol_db = protocol.ProtocolDatabase(self.context['protocol_db']) for transaction_id in protocol_db.get_abortable_transactions(): logger.info( f"Working on to-be-aborted transaction {transaction_id}.") self._abort_transaction(protocol_db, transaction_id) for transaction_id in protocol_db.get_prepared_transactions(): logger.info(f"Working on prepared transaction {transaction_id}.") self._recover_transaction(protocol_db, transaction_id) # We are done with recovery. recovery_conn.close() protocol_db.close() self.state = TransactionManagerStates.INITIALIZE
def test_transaction_abort(self): transaction_id = str(uuid.uuid4()) coordinator_pdb = protocol.ProtocolDatabase('coordinator_' + self.test_file) participant_pdb = protocol.ProtocolDatabase('participant_' + self.test_file) coordinator_pdb.log_initialize_of(transaction_id, TransactionRole.COORDINATOR) participant_pdb.log_initialize_of(transaction_id, TransactionRole.PARTICIPANT) self.assertEqual(coordinator_pdb.get_role_in(transaction_id), TransactionRole.COORDINATOR) self.assertEqual(participant_pdb.get_role_in(transaction_id), TransactionRole.PARTICIPANT) coordinator_pdb.log_abort_of(transaction_id) participant_pdb.log_abort_of(transaction_id) coordinator_pdb.close() participant_pdb.close() time.sleep(0.5)
def test_site_awareness(self): transaction_id = str(uuid.uuid4()) coordinator_pdb = protocol.ProtocolDatabase('coordinator_' + self.test_file) participant_pdb = protocol.ProtocolDatabase('participant_' + self.test_file) coordinator_pdb.log_initialize_of(transaction_id, TransactionRole.COORDINATOR) participant_pdb.log_initialize_of(transaction_id, TransactionRole.PARTICIPANT) coordinator_pdb.add_participant(transaction_id, 1) coordinator_pdb.add_participant(transaction_id, 2) participant_pdb.add_coordinator(transaction_id, 2) participants = coordinator_pdb.get_participants_in(transaction_id) self.assertEqual(len(participants), 2) self.assertIn(1, participants) self.assertIn(2, participants) coordinator = participant_pdb.get_coordinator_for(transaction_id) self.assertEqual(coordinator, 2) coordinator_pdb.close() participant_pdb.close() time.sleep(0.5)
def test_transaction_recovery(self): transaction_id_1 = str(uuid.uuid4()) transaction_id_2 = str(uuid.uuid4()) coordinator_pdb = protocol.ProtocolDatabase('coordinator_' + self.test_file) coordinator_pdb.log_initialize_of(transaction_id_1, TransactionRole.COORDINATOR) coordinator_pdb.log_initialize_of(transaction_id_2, TransactionRole.COORDINATOR) coordinator_pdb.log_prepare_of(transaction_id_1) abortable_transactions = coordinator_pdb.get_abortable_transactions() prepared_transactions = coordinator_pdb.get_prepared_transactions() self.assertEqual(len(abortable_transactions), 1) self.assertEqual(len(prepared_transactions), 1) self.assertEqual(abortable_transactions[0], transaction_id_2) self.assertEqual(prepared_transactions[0], transaction_id_1) coordinator_pdb.close() time.sleep(0.5)
def __init__(self, transaction_id: str, client_socket: socket.socket, **context): communication.GenericSocketUser.__init__(self, client_socket) threading.Thread.__init__(self, daemon=True) self.protocol_db = protocol.ProtocolDatabase(context['protocol_db']) self.transaction_coordinator = context['transaction_coordinator'] self.context = context # Setup a connection to the RM (i.e. Postgres). self.conn = psycopg2.connect(user=context['postgres_username'], password=context['postgres_password'], host=context['postgres_hostname'], database=context['postgres_database']) self.transaction_id = psycopg2.extensions.Xid.from_string( transaction_id) self.conn.autocommit = False self.conn.tpc_begin(self.transaction_id) # To enter the PREPARE / ABORT state instead, parent must explicitly change the state after instantiation. self.state = ParticipantStates.INITIALIZE self.socket_token = queue.Queue(1) self.previous_edge_property = None self.is_prepared = False
def test_recovery_coordinator(self): transaction_id_1 = str(uuid.uuid4()) conn = self.get_postgres_connection() conn.tpc_begin(psycopg2.extensions.Xid.from_string(transaction_id_1)) cur = conn.cursor() cur.execute(""" INSERT INTO thermometerobservation VALUES ('a239a033-b340-426d-a686-ad32908709ae', 48, '2017-11-08 00:00:00', '9592a785_d3a4_4de2_bc3d_cfa1a127bf40'); """) # Coordinator knows COMMIT, but does not have the ACK. pdb1 = protocol.ProtocolDatabase(self.test_file + '1') pdb1.log_initialize_of(transaction_id_1, TransactionRole.COORDINATOR) pdb1.add_participant(transaction_id_1, 1) pdb1.log_commit_of(transaction_id_1) conn.tpc_prepare() pdb1.close() class TestRM(object): def tpc_recover(self): return [] def close(self): pass manager_thread_1 = manager.TransactionManagerThread( **self.get_postgres_context(), protocol_db=self.test_file + '1', role_factory=_TestCoordinatorRecoveryTransactionStateFactory( transaction_id_1=transaction_id_1), site_alias=socket.gethostname(), node_port=self.test_port + 5, site_list=[{ 'alias': socket.gethostname(), 'hostname': socket.gethostname(), 'port': self.test_port + 5 }, { 'alias': socket.gethostname(), 'hostname': socket.gethostname(), 'port': self.test_port + 6 }]) manager_thread_2 = manager.TransactionManagerThread( **self.get_postgres_context(), test_rm=TestRM(), protocol_db=self.test_file, role_factory=_TestDummyTransactionRoleFactory(), site_alias=socket.gethostname(), node_port=self.test_port + 6, site_list=[{ 'alias': socket.gethostname(), 'hostname': socket.gethostname(), 'port': self.test_port + 5 }, { 'alias': socket.gethostname(), 'hostname': socket.gethostname(), 'port': self.test_port + 6 }]) manager_thread_2.start() time.sleep(1.0) manager_thread_1.start() time.sleep(0.5) # Create new connection to TM, and issue the shutdown. client_socket = communication.GenericSocketUser() client_socket.socket.connect( (socket.gethostname(), self.test_port + 5)) client_socket.send_op(OpCode.SHUTDOWN) time.sleep(0.5) client_socket.socket.close() manager_thread_1.join() manager_thread_2.join()