def test_default_state(): fake_request(10.0) assert pmnc.state.get("") is None assert pmnc.state.get("", "default") == "default" pmnc.state.set("", None) assert pmnc.state.get("") is None assert pmnc.state.get("", "default") is None pmnc.state.delete("") assert pmnc.state.get("", "default") == "default" # and now for str/bytes support pmnc.state.set(rus, rus + rus) assert pmnc.state.get(rus) == rus + rus pmnc.state.delete(rus) with expected(InputParameterError): pmnc.state.set(rus_b, rus) with expected(InputParameterError): pmnc.state.get(rus_b) with expected(InputParameterError): pmnc.state.delete(rus_b) pmnc.state.set(rus, rus_b) assert pmnc.state.get(rus) == rus_b pmnc.state.delete(rus)
def caller(*args, **kwargs): fake_request(6.0) nonlocal request_id, response request_id = pmnc.request.unique_id pmnc.request.parameters["AAA"] = "BBB" pmnc.request.describe("my request") response = pmnc.__getattr__(__name__).execute_reverse("good_cage", "module", "method", args, kwargs)
def test_module_state(): fake_request(10.0) module_state_dir = _module_state_dir("foo") module_state = ModuleState(module_state_dir) try: # note that the first access to a queue or a database creates it, # therefore options such as re_len are required, but subsequent # accesses just open it and the options can be omitted assert not os_path.exists(os_path.join(module_state_dir, "state.queue")) queue = module_state.get_queue_db("state", re_len=100) assert os_path.exists(os_path.join(module_state_dir, "state.queue")) stat = queue.stat() assert "buckets" not in stat queue.append("foo") assert module_state.get_queue_db("state") is queue assert module_state.get_queue_db("state2") is not queue assert not os_path.exists(os_path.join(module_state_dir, "state.btree")) btree = module_state.get_btree_db("state") assert os_path.exists(os_path.join(module_state_dir, "state.btree")) stat = btree.stat() assert "buckets" not in stat with expected(bsddb.DBInvalidArgError): btree.append("foo") assert module_state.get_btree_db("state") is btree assert module_state.get_btree_db("state2") is not btree finally: module_state.close()
def test_connect_fails(): # connect fails fake_request(1.0) xa = pmnc.transaction.create() xa.callable_2(**hooks_).execute() try: xa.execute() except ResourceError as e: assert by_regex("^(?:int )?division (?:or modulo )?by zero$")(str(e)) assert e.participant_index == 0 and e.terminal and e.recoverable else: assert False # connect hangs fake_request(1.0) xa = pmnc.transaction.create() xa.callable_3(**hooks_).execute() try: xa.execute() except TransactionExecutionError as e: assert str(e) == "request deadline waiting for intermediate result from " \ "resource callable_3 in transaction {0:s}".format(xa) assert e.participant_index == 0 else: assert False
def test_execute_success(): fake_request(10.0) request_id = None response = None def caller(*args, **kwargs): fake_request(6.0) nonlocal request_id, response request_id = pmnc.request.unique_id pmnc.request.parameters["AAA"] = "BBB" pmnc.request.describe("my request") response = pmnc.__getattr__(__name__).execute_reverse("good_cage", "module", "method", args, kwargs) assert "good_cage" not in _rq_queues th = HeavyThread(target = caller, args = (1, "foo"), kwargs = { "biz": "baz" }) th.start() try: sleep(2.0) assert "good_cage" in _rq_queues assert request_id in _rs_queues req_id, req = _rq_queues["good_cage"].pop() assert req_id == request_id assert abs(req["request"].pop("deadline") - time() - 4.0) < 1.0 assert req == dict \ ( source_cage = __cage__, target_cage = "good_cage", module = "module", method = "method", args = (1, "foo"), kwargs = { "biz": "baz" }, request = dict(protocol = pmnc.request.protocol, interface = pmnc.request.interface, unique_id = request_id, description = "my request", parameters = dict(auth_tokens = {}, AAA = "BBB")), ) _rs_queues[request_id].push({ "result": "RESULT" }) sleep(2.0) assert "good_cage" in _rq_queues assert request_id not in _rs_queues finally: th.stop() assert response == "RESULT" assert "good_cage" in _rq_queues assert request_id not in _rs_queues
def test_failure(): def process_revrpc_request(module, method, args, kwargs): 1 / 0 with active_interface("revrpc", **interface_config(process_revrpc_request = process_revrpc_request)): fake_request(3.0) request_dict = pmnc.request.to_dict() request = dict(source_cage = "source_cage", target_cage = __cage__, module = "module", method = "method", args = (), kwargs = {}, request = request_dict) poll_queue.push(request) request_id, response = post_queue.pop(3.0) assert request_id == pmnc.request.unique_id error = response.pop("exception") assert not response assert error.startswith("ZeroDivisionError(")
def test_success(): fake_request(10.0) def execute(resource, *args, **kwargs): resource._count += 1 resource._q.push(("execute", resource._count, args, kwargs)) return "ok" # success sequence: connect, begin_transaction, execute, commit (then the instance is put back to the pool) xa = pmnc.transaction.create(biz = "baz") xa.callable_1("abc", begin_transaction = begin_transaction, execute = execute, commit = commit, rollback = rollback, foo = "bar").execute(1, 2, eee = "fff") assert xa.execute() == ("ok", ) # now check the trace assert q.pop(0.0) == ("connect", 0, { "param1": "value1", "param2": "value2" }) m, c, args, kwargs = q.pop(0.0) assert m == "begin_transaction" and c == 1 assert args == (xa._xid, ) assert kwargs == dict(transaction_options = { "biz": "baz" }, source_module_name = __name__, resource_args = ("abc", ), resource_kwargs = { "foo": "bar" }) assert q.pop(0.0) == ("execute", 2, (1, 2), { "eee": "fff" }) assert q.pop(0.0) == ("commit", 3) # commit is waited upon, therefore "commit" is in the queue assert q.pop(1.0) is None
def _poller_proc(self, source_cage): while not current_thread().stopped(): try: # attach a new fake request to this thread so that # network access in RPC call times out properly fake_request(timeout = self._request_timeout, interface = self._name) pmnc.request.describe("polling cage {0:s}".format(source_cage)) # fetch a request from the remote cage # and extract the call information from it try: revrpc_request = self._poll(source_cage) except: pmnc.log.error(exc_string()) # failure to fetch a request current_thread().stopped(self._request_timeout) # results in idle delay continue if revrpc_request is None: continue elif current_thread().stopped(): break assert revrpc_request.pop("target_cage") == __cage__, "expected call to this cage" assert revrpc_request.pop("source_cage") == source_cage, "expected call from {0:s}".format(source_cage) self._process_request(source_cage, revrpc_request) except: pmnc.log.error(exc_string())
def test_failure(): fake_request(10.0) def execute(resource, *args, **kwargs): 1 / 0 # failure sequence (the instance is reused): begin_transaction, execute, rollback, disconnect xa = pmnc.transaction.create(biz = "baz") xa.callable_1("abc", begin_transaction = begin_transaction, execute = execute, commit = commit, rollback = rollback, foo = "bar").execute(1, 2, eee = "fff") try: xa.execute() except ResourceError as e: assert by_regex("^(?:int )?division (?:or modulo )?by zero$")(str(e)) assert not e.recoverable and e.terminal else: assert False # now check the trace m, c, args, kwargs = q.pop(0.0) assert m == "begin_transaction" and c == 4 assert args == (xa._xid, ) assert kwargs == dict(transaction_options = { "biz": "baz" }, source_module_name = __name__, resource_args = ("abc", ), resource_kwargs = { "foo": "bar" }) assert q.pop(1.0) == ("rollback", 5) # rollback is not waited upon, therefore "rollback" may not appear in the queue immediately assert q.pop(1.0) == ("disconnect", 6) assert q.pop(1.0) is None
def test_get_set_delete(): fake_request(10.0) xa = pmnc.transaction.create() xa.state.get("key") assert xa.execute() == (None,) xa = pmnc.transaction.create() xa.state.get("key", "default") assert xa.execute() == ("default",) xa = pmnc.transaction.create() xa.state.set("key", "value") assert xa.execute() == (None,) xa = pmnc.transaction.create() xa.state.get("key") assert xa.execute() == ("value",) xa = pmnc.transaction.create() xa.state.get("key", "default") assert xa.execute() == ("value",) xa = pmnc.transaction.create() xa.state.delete("key") assert xa.execute() == (None,) xa = pmnc.transaction.create() xa.state.get("key") assert xa.execute() == (None,) xa = pmnc.transaction.create() xa.state.get("key", "default") assert xa.execute() == ("default",)
def test_bytes_str(): fake_request(5.0) xa = pmnc.transaction.create() xa.state.set(rus, rus + rus) assert xa.execute() == (None,) xa = pmnc.transaction.create() xa.state.get(rus) assert xa.execute() == (rus + rus,) xa = pmnc.transaction.create() xa.state.delete(rus) assert xa.execute() == (None,) xa = pmnc.transaction.create() xa.state.get(rus_b) with expected(ResourceInputParameterError): xa.execute() xa = pmnc.transaction.create() xa.state.set(rus_b, "value") with expected(ResourceInputParameterError): xa.execute() xa = pmnc.transaction.create() xa.state.delete(rus_b) with expected(ResourceInputParameterError): xa.execute()
def test_queue_extent_reuse(): fake_request(10.0) def current_extents(): return sorted( [ int(s.split(".")[-1]) for s in listdir(_module_state_dir(__name__)) if by_regex("^.*\\.ext_queue\\.queue\\.[0-9]+$")(s) ] ) q = pmnc.state.get_queue("ext_queue", pagesize=8192, re_len=1024, q_extentsize=2) v = 0 for i in range(64): # fill up more than one extent push(q, v) v += 1 extents_before = current_extents() assert len(extents_before) > 1 w = 0 t = Timeout(5.0) while not t.expired: # keep popping and pushing to have the extents reused push(q, v) v += 1 assert pop(q) == w w += 1 extents_after = current_extents() assert extents_before[0] < extents_after[0]
def test_partial_commit(): def accept_anything(xa, results): for value in results: if value is not xa.NoValue: return value fake_request(2.0) xa = pmnc.transaction.create(accept = accept_anything) xa.callable_1(execute = lambda self: 1).execute() xa.callable_1(execute = lambda self: 1 / 0).execute() try: xa.execute() except TransactionCommitError as e: assert str(e) == "transaction {0:s} got unexpected commit outcome " \ "from resource callable_1: rollback".format(xa) assert e.participant_index == 1 else: assert False fake_request(2.0) xa = pmnc.transaction.create(accept = accept_anything, sync_commit = False) xa.callable_1(execute = lambda self: 1).execute() xa.callable_1(execute = lambda self: 1 / 0).execute() xa.execute()
def self_test(): from pmnc.request import fake_request fake_request(10.0) xa = pmnc.transaction.create() xa.void.do_stuff() assert xa.execute() == (None,)
def test_transaction_rate(): fake_request(4.0) t = Timeout(2.0) while not t.expired: xa = pmnc.transaction.create() xa.execute() xa = pmnc.transaction.create() xa.execute() assert xa.get_transaction_rate() > 1.0
def test_request_processing(): fake_request(1.0) with pmnc.performance.request_processing(): pass with expected(ZeroDivisionError): with pmnc.performance.request_processing(): 1 / 0
def test_secondary_index(): fake_request(30.0) def _insert(txn, db, idx, primary_key, secondary_key, value): db.put(primary_key.encode("unicode-escape"), pickle(value), txn) idx.put(secondary_key.encode("unicode-escape"), pickle(primary_key), txn) def _lookup_primary_key(txn, db, primary_key): value = db.get(primary_key.encode("unicode-escape"), None, txn) if value is not None: return unpickle(value) def _lookup_secondary_key(txn, db, idx, secondary_key): primary_key = idx.get(secondary_key.encode("unicode-escape"), None, txn) if primary_key is not None: return _lookup_primary_key(txn, db, unpickle(primary_key)) N = 100 pks = [str(i) for i in range(N)] sks = [str(urandom(8)) for i in range(N)] values = [urandom(randint(1, 2048)) for i in range(N)] # populate the database db = pmnc.state.get_database("indexed_database") idx = pmnc.state.get_database("indexed_database") start = time() for i in range(N): pmnc.state.implicit_transaction(_insert, db, idx, pks[i], sks[i], values[i]) pmnc.log.info("indexed state: {0:d} insert(s)/sec.".format(int(N / ((time() - start) or 0.01)))) # lookup by the primary index db = pmnc.state.get_database("indexed_database") start = time() for i in range(N): assert pmnc.state.implicit_transaction(_lookup_primary_key, db, pks[i]) == values[i] pmnc.log.info("indexed state: {0:d} primary lookup(s)/sec.".format(int(N / ((time() - start) or 0.01)))) assert pmnc.state.implicit_transaction(_lookup_primary_key, db, "") is None # lookup by the primary index db = pmnc.state.get_database("indexed_database") idx = pmnc.state.get_database("indexed_database") start = time() for i in range(N): assert pmnc.state.implicit_transaction(_lookup_secondary_key, db, idx, sks[i]) == values[i] pmnc.log.info("indexed state: {0:d} secondary lookup(s)/sec.".format(int(N / ((time() - start) or 0.01)))) assert pmnc.state.implicit_transaction(_lookup_secondary_key, db, idx, "") is None
def test_large_values(): fake_request(10.0) for i in range(20): value = "*" * (1 << i) pmnc.state.set(str(i), value) for i in range(20): value = "*" * (1 << i) assert pmnc.state.get(str(i)) == value
def test_running(): fake_request(90.0) start = time() for i in range(901): pmnc.performance.event("resource.baz.response_rate.success") pmnc.performance.sample("resource.baz.response_time.success", 1) sleep(0.1) if i % 10 == 0: pmnc.log("wait {0:d}/90".format(i // 10)) pmnc.performance.extract()
def test_resource_decline(): fake_request(0.4) # less than pool__min_time xa = pmnc.transaction.create() xa.void.do_stuff() try: xa.execute() except ResourceError as e: assert str(e).startswith("transaction {0:s} is declined by resource instance void/".format(xa)) assert e.participant_index == 0 and e.recoverable and not e.terminal else: assert False
def test_performance(): N = 256 def threads(n, f): ths = [ Thread(target = f, args = (N // n, )) for i in range(n) ] start = time() for th in ths: th.start() for th in ths: th.join() return int(N / (time() - start)) def test_transaction(n): for i in range(n): fake_request(10.0) xa = pmnc.transaction.create() xa.void.do_stuff() xa.execute() def state_transaction(n): for i in range(n): fake_request(30.0) xa = pmnc.transaction.create() xa.state.set(str(randint(0, 1000000)), i) xa.execute() fake_request(60.0) pmnc.log("begin performance test (may take a few minutes)") pmnc._loader.set_log_priority(4) try: test_1 = threads(1, test_transaction) test_4 = threads(4, test_transaction) test_16 = threads(16, test_transaction) test_64 = threads(64, test_transaction) pmnc.log("{0:d}/{1:d}/{2:d}/{3:d} empty transaction(s) per second".\ format(test_1, test_4, test_16, test_64)) state_1 = threads(1, state_transaction) state_4 = threads(4, state_transaction) state_16 = threads(16, state_transaction) state_64 = threads(64, state_transaction) pmnc.log("{0:d}/{1:d}/{2:d}/{3:d} state transaction(s) per second".\ format(state_1, state_4, state_16, state_64)) finally: pmnc._loader.set_log_priority(6) pmnc.log("end performance test")
def test_execute_fails(): # execute hangs fake_request(1.0) def execute(res, *args, **kwargs): sleep(2.0) hooks = hooks_.copy(); hooks["execute"] = execute xa = pmnc.transaction.create() xa.callable_1(**hooks).execute() try: xa.execute() except TransactionExecutionError as e: assert str(e) == "request deadline waiting for intermediate result from " \ "resource callable_1 in transaction {0:s}".format(xa) assert e.participant_index == 0 else: assert False assert q.pop(0.0)[:2] == ("connect", 0) assert q.pop(0.0)[:2] == ("begin_transaction", 1) assert q.pop(0.5) is None # this is where execute is called assert q.pop(1.0)[:2] == ("rollback", 2) assert q.pop(1.0) is None # note that the resource instance had not failed and is reused # execute throws fake_request(1.0) def execute(res, *args, **kwargs): 1 / 0 hooks = hooks_.copy(); hooks["execute"] = execute xa = pmnc.transaction.create() xa.callable_1(**hooks).execute() try: xa.execute() except ResourceError as e: assert by_regex("^(?:int )?division (?:or modulo )?by zero$")(str(e)) assert e.participant_index == 0 and e.terminal and not e.recoverable else: assert False assert q.pop(0.0)[:2] == ("begin_transaction", 3) assert q.pop(1.0)[:2] == ("rollback", 4) assert q.pop(1.0)[:2] == ("disconnect", 5) assert q.pop(1.0) is None
def test_commit_fails(): # commit hangs fake_request(1.0) def commit(res, *args, **kwargs): sleep(2.0) hooks = hooks_.copy(); hooks["commit"] = commit xa = pmnc.transaction.create() xa.callable_1(**hooks).execute() try: xa.execute() except TransactionCommitError as e: assert str(e) == "request deadline waiting for commit from resource " \ "callable_1 in transaction {0:s}".format(xa) assert e.participant_index == 0 else: assert False assert q.pop(0.0)[:2] == ("connect", 0) assert q.pop(0.0)[:2] == ("begin_transaction", 1) assert q.pop(0.0)[:2] == ("execute", 2) assert q.pop(1.5) is None # this is where commit is called # note that the resource instance had not failed and is reused # commit throws fake_request(1.0) def commit(res, *args, **kwargs): 1 / 0 hooks = hooks_.copy(); hooks["commit"] = commit xa = pmnc.transaction.create() xa.callable_1(**hooks).execute() try: xa.execute() except TransactionCommitError as e: assert str(e) == "transaction {0:s} got unexpected commit outcome " \ "from resource callable_1: failure".format(xa) assert e.participant_index == 0 else: assert False assert q.pop(0.0)[:2] == ("begin_transaction", 3), x assert q.pop(0.0)[:2] == ("execute", 4) assert q.pop(1.0)[:2] == ("disconnect", 5) assert q.pop(1.0) is None
def test_queue_many_items(): fake_request(60.0) q = pmnc.state.get_queue("queue_many_items", re_len=64) counter = InterlockedCounter() def _push_n(txn, n): for i in range(n): c = counter.next() _push(txn, q, "test-{0:d}".format(c), pickle) def push_n(n): pmnc.state.implicit_transaction(_push_n, n) # push as much as we can per single transaction for i in range(8): push_n(1024) push_n(2048) push_n(4096) push_n(8100) # until we hit the limit of max_objects (8192) for one transaction with expected(MemoryError): push_n(8192) Timeout(4.0).wait() # wait for checkpoint # now pop everything off the queue def _pop_n(txn, n): for i in range(n): assert _pop(txn, q, None, unpickle) is not None def pop_n(n): pmnc.state.implicit_transaction(_pop_n, n) assert peek(q) == "test-0" for i in range(8): pop_n(8100) pop_n(4096) pop_n(2048) pop_n(1024) # the attempt to push 8192 records should have left no trace assert peek(q) is None
def test_rollback_fails(): # rollback hangs fake_request(1.0) def execute(res, *args, **kwargs): 1 / 0 def rollback(res, *args, **kwargs): sleep(2.0) hooks = hooks_.copy(); hooks["execute"] = execute; hooks["rollback"] = rollback xa = pmnc.transaction.create() xa.callable_1(**hooks).execute() try: xa.execute() except ResourceError as e: assert by_regex("^(?:int )?division (?:or modulo )?by zero$")(str(e)) assert e.participant_index == 0 and e.terminal and not e.recoverable else: assert False assert q.pop(0.0)[:2] == ("connect", 0) assert q.pop(0.0)[:2] == ("begin_transaction", 1) assert q.pop(1.0) is None # this is where rollback is called assert q.pop(2.0)[:2] == ("disconnect", 2) # rollback throws fake_request(1.0) def execute(res, *args, **kwargs): 1 / 0 def rollback(res, *args, **kwargs): {}["not there"] hooks = hooks_.copy(); hooks["execute"] = execute; hooks["rollback"] = rollback xa = pmnc.transaction.create() xa.callable_1(**hooks).execute() try: xa.execute() except ResourceError as e: assert by_regex("^(?:int )?division (?:or modulo )?by zero$")(str(e)) assert e.participant_index == 0 and e.terminal and not e.recoverable else: assert False assert q.pop(0.0)[:2] == ("connect", 0) assert q.pop(0.0)[:2] == ("begin_transaction", 1) assert q.pop(1.0)[:2] == ("disconnect", 2)
def _start_pmnc(test_cages_dir): test_cage_dir = os_path.join(test_cages_dir, cage_name) Request._self_test = main_module_name fake_request() pmnc = ModuleLoader(node_name, cage_name, test_cage_dir, _log, 6) try: pmnc.startup.start() except: pmnc.startup.stop() raise else: return pmnc
def test_no_accept(): fake_request(1.0) def no_accept(xa, results): return None xa = pmnc.transaction.create(accept = no_accept) xa.void.do_stuff() try: xa.execute() except TransactionExecutionError as e: assert str(e) == "intermediate results of transaction {0:s} have not been accepted".format(xa) assert e.participant_index is None else: assert False
def test_errors(): fake_request(1.0) def execute(res, *args, **kwargs): raise ResourceError(code = 1, description = "foo", recoverable = True) # but terminal by default hooks = hooks_.copy(); hooks["execute"] = execute xa = pmnc.transaction.create() xa.callable_1(**hooks).execute() try: xa.execute() except ResourceError as e: assert str(e) == "1: foo" and e.code == 1 and e.description == "foo" assert e.participant_index == 0 and e.terminal and e.recoverable else: assert False assert q.pop(0.0)[:2] == ("connect", 0) assert q.pop(0.0)[:2] == ("begin_transaction", 1) assert q.pop(1.0)[:2] == ("rollback", 2) assert q.pop(1.0)[:2] == ("disconnect", 3) assert q.pop(1.0) is None fake_request(1.0) def execute(res, *args, **kwargs): raise SQLResourceError(description = "bar", state = "P0001", recoverable = False) # and terminal by default hooks = hooks_.copy(); hooks["execute"] = execute xa = pmnc.transaction.create() xa.callable_1(**hooks).execute() try: xa.execute() except SQLResourceError as e: assert str(e) == "[P0001] bar" and e.state == "P0001" and e.description == "bar" assert e.participant_index == 0 and e.terminal and not e.recoverable else: assert False assert q.pop(0.0)[:2] == ("connect", 0) assert q.pop(0.0)[:2] == ("begin_transaction", 1) assert q.pop(1.0)[:2] == ("rollback", 2) assert q.pop(1.0)[:2] == ("disconnect", 3) assert q.pop(1.0) is None
def test_pool_exhaust(): fake_request(1.0) xa = pmnc.transaction.create() xa.void.do_stuff() xa.void.do_stuff() xa.void.do_stuff() xa.void.do_stuff() # one too many, pool__size = 3, results in deadlock try: xa.execute() except TransactionExecutionError as e: assert str(e) == "request deadline waiting for intermediate result " \ "from resource void in transaction {0:s}".format(xa) assert 0 <= e.participant_index <= 3 else: assert False
def test_queue(): fake_request(10.0) q = pmnc.state.get_queue("some_queue", re_len=1024) assert pop(q, "default") == "default" push(q, None) assert pop(q, "default") is None assert pop(q, "default") == "default" # and now for str/bytes support push(q, rus) assert pop(q) == rus push(q, rus_b) assert pop(q) == rus_b
def test_send_many(): def process_request(request, response): pass with active_interface("smpp_1", **interface_config(process_request = process_request)): sleep(3.0) # to allow connection to spin up fake_request(30.0) xa = pmnc.transaction.create() xa.smpp_1.submit_sm(dest_addr_ton = DEST_TON, dest_addr_npi = DEST_NPI, destination_addr = DEST_ADDR, short_message = "test1") xa.smpp_1.submit_sm(dest_addr_ton = DEST_TON, dest_addr_npi = DEST_NPI, destination_addr = DEST_ADDR, short_message = "test2") xa.smpp_1.submit_sm(dest_addr_ton = DEST_TON, dest_addr_npi = DEST_NPI, destination_addr = DEST_ADDR, short_message = "test3") xa.execute() sleep(10.0)
def test_large_queue_items(): fake_request(10.0) q = pmnc.state.get_queue("large_items", re_len=1024, re_pad=0x5A) for i in (16, 64, 256, 1024): value = b"*" * i push(q, value, lambda x: x) with expected(bsddb.DBInvalidArgError): push(q, b"*" * 1025, lambda x: x) for i in (16, 64, 256, 1024): value = b"*" * i + b"\x5a" * (1024 - i) assert pop(q, None, lambda x: x) == value assert pop(q, None, lambda x: x) is None
def _start_pmnc(test_cages_dir): test_cage_dir = os_path.join(test_cages_dir, cage_name) Request._self_test = main_module_name fake_request() loader = ModuleLoader(node_name, cage_name, test_cage_dir, _log, "NOISE", 0.0, 0.0) pmnc = ModuleLoaderProxy(loader, __name__) try: pmnc.startup.start() except: pmnc.startup.stop() raise else: return pmnc
def test_queue_rollback_disorder(): fake_request(10.0) q = pmnc.state.get_queue("queue_abort_reverse", re_len=128) push(q, 1) push(q, 2) # first reader rolls back evt1 = Event() def txn1(txn): assert _pop(txn, q, None, unpickle) == 1 evt1.set() evt2.wait() 1 / 0 # this causes rollback of the first transaction # second reader commits evt2 = Event() def txn2(txn): evt1.wait() result = _pop(txn, q, None, unpickle) evt2.set() return result def th_proc(): fake_request(10.0) with expected(ZeroDivisionError): pmnc.state.implicit_transaction(txn1) # in presence of multiple failing readers the order is not preserved th = Thread(target=th_proc) th.start() try: assert pmnc.state.implicit_transaction(txn2) == 2 finally: th.join() assert pmnc.state.implicit_transaction(txn2) == 1
def test_data_type(t, vs): tn = "table_{0:s}".format(random_string(8)) sqls = [ "CREATE TABLE {0:s} (i number(8), v {1:s})".format(tn, t) ] params = {} for i, v in enumerate(vs): sqls.append("INSERT INTO {0:s} (i, v) VALUES ({1:d}, {{v{1:d}}})".format(tn, i)) params["v{0:d}".format(i)] = v sqls.append("SELECT v FROM {0:s} ORDER BY i".format(tn)) sqls.append("DROP TABLE {0:s}".format(tn)) fake_request(30.0) xa = pmnc.transaction.create() xa.oracle_1.execute(*sqls, **params) records = xa.execute()[0][-2] result = [ r["V"] for r in records ] # note that field name is capitalized return result
def test_query_cursor(): fake_request(10.0) rs = pmnc.transaction.mongodb_1.test.find() assert len(rs.documents) == 1 and rs.cursor_id == 0 rs = pmnc.transaction.mongodb_1.test.find(docs_to_return=50) assert len(rs.documents) == 50 and rs.cursor_id != 0 xa = pmnc.transaction.create() xa.mongodb_1.test.get_more(rs.cursor_id, docs_to_return=25) xa.mongodb_1.test.get_more(rs.cursor_id, docs_to_return=25) rs1, rs2 = xa.execute() assert len(rs1.documents) == 25 and len(rs2.documents) == 25 ds = [d["k"] for d in (rs.documents + rs1.documents + rs2.documents)] ds.sort() assert ds == list(range(100))
def test_performance(): fake_request(120.0) for i in range(90): pmnc.performance.event("interface.foo.response_rate.success") pmnc.performance.sample("interface.foo.response_time.success", 10) pmnc.performance.event("resource.bar.transaction_rate.success") pmnc.performance.sample("resource.bar.processing_time.success", 10) sleep(1.0) pmnc.log("wait {0:d}/90".format(i + 1)) request = dict(url="/?" "interface.foo.request_rate=expanded&" "interface.foo.response_rate.success=collapsed&" "interface.foo.response_time=expanded&" "interface.foo.response_time.success=collapsed&" "resource.bar.transaction_rate=expanded&" "resource.bar.transaction_rate.success=collapsed&" "resource.bar.processing_time=expanded&" "resource.bar.processing_time.success=collapsed", method="GET", headers={}, body=b"") response = dict(status_code=200, headers={}, body=b"") pmnc.__getattr__(__name__).process_request(request, response) assert response["status_code"] == 200 content = response["content"] # difficult to assert anything reasonable about the response content here assert "10.0" in content and "100" in content assert "interface foo (successful response rate)" in content assert "interface foo (successful response time)" in content assert "resource bar (successful transaction rate)" in content assert "resource bar (successful processing time)" in content assert "request rate</a>" in content assert "response time</a>" in content assert "transaction rate</a>" in content assert "processing time</a>" in content assert "RAM" in content assert "CPU" in content
def test_resource_echo(): fake_request(1.0) echo = Resource("echo/1", executable="echo", arguments=(), environment={}) echo.connect() try: assert echo.expired echo.set_pool_info("echo", 1) retcode, stdout, stderr = \ echo.execute("foo", 123, Decimal("1.00")) assert retcode == 0 assert stdout.rstrip() == b"foo 123 1.00" assert stderr == b"" finally: echo.disconnect()
def test_resource(): def process_xmlrpc_request(request, response): if request["method"] == "ShouldBe.Failing": raise Exception(request["args"][0]) else: response["result"] = request, pmnc.request.parameters[ "auth_tokens"] with active_interface( "xmlrpc_1", **interface_config( process_xmlrpc_request=process_xmlrpc_request)): fake_request(10.0) for i in range(16): s = "*" * 2**i n = "n" + str(i) result = pmnc.transaction.xmlrpc_1.Module.Method( i, s, [s], { s: i, n: None }) assert result == [{ "method": "Module.Method", "args": [i, s, [s], { s: i, n: None }] }, { "username": "******", "peer_ip": "127.0.0.1", "password": "******", "encrypted": False }] try: pmnc.transaction.xmlrpc_1.ShouldBe.Failing("some error") except ResourceError as e: assert e.code == 500 and e.description.startswith( "Exception(\"some error\")") assert not e.recoverable and not e.terminal
def test_international(): fake_request(10.0) russian = "������¨�������������������������‗אבגדהו¸זחטיךכלםמןנסעףפץצקרש��ת�" assert pmnc.transaction.mysql_1.execute("SELECT {s} AS s", s = russian) == \ ([{ "s": russian }],) # assuming CIS collation in WHERE clause tn = random_name() rs = pmnc.transaction.mysql_1.execute( "CREATE TABLE {0:s} (s VARCHAR(66))".format(tn), "INSERT INTO {0:s} (s) VALUES ({{s}})".format(tn), "SELECT s FROM {0:s} WHERE s = {{s}}".format(tn), "SELECT LOWER(s) AS sl FROM {0:s} WHERE s = {{sl}}".format(tn), "DROP TABLE {0:s}".format(tn), s = russian, sl = russian.lower()) assert rs == ([], [], [{ "s": russian }], [{ "sl": russian.lower() }], [])
def test_sp(): fake_request(10.0) tn = random_name() rs = pmnc.transaction.mysql_1.execute("""\ CREATE PROCEDURE {0:s}(x int) BEGIN SELECT x AS x; END """.format(tn), "CALL {0:s}({{one}})".format(tn), "CALL {0:s}({{null}})".format(tn), "DROP PROCEDURE {0:s}".format(tn), one = 1, null = None) # this is awkward but SELECT NULL returns nothing assert rs == ([], [{ "x": 1 }], [], [])
def test_resource_read(): fn = random_filename() fake_request(3.0) pmnc.transaction.file_1.write(fn, b"foobar") assert pmnc.transaction.file_1.read(fn) == b"foobar" assert os_path.isfile(target_filename(fn)) assert pmnc.transaction.file_1.read(fn, remove = True, frag_size = 4) == b"foobar" assert not os_path.isfile(target_filename(fn)) with expected(ResourceError, "^.*never_existed.*$"): pmnc.transaction.file_1.read("never_existed") pmnc.transaction.file_1.write(fn, b"\x00" * 33554432) fake_request(0.1) with expected(Exception, "^request deadline.*$"): pmnc.transaction.file_1.read(fn, frag_size = 1)
def test_notify(): fake_request(10.0) pmnc.notify.info("info") pmnc.notify.warning("warning") pmnc.notify.error("error") pmnc.notify.alert("alert") assert len(notifications) == 4 extracted_notifications = pmnc.notify.extract() assert extracted_notifications is not notifications assert [ (type(d["timestamp"]), d["level"], d["message"]) for d in extracted_notifications ] == \ [ (int, "INFO", "info"), (int, "WARNING", "warning"), (int, "ERROR", "error"), (int, "ALERT", "alert") ] pmnc.notify.alert("again") assert len(notifications) == 4 extracted_notifications = pmnc.notify.extract() assert [ (type(d["timestamp"]), d["level"], d["message"]) for d in extracted_notifications ] == \ [ (int, "WARNING", "warning"), (int, "ERROR", "error"), (int, "ALERT", "alert"), (int, "ALERT", "again") ]
def test_process_one(): loopback_queue = InterlockedQueue() def process_request(request, response): loopback_queue.push(request) with active_interface("jms_1", **interface_config(process_request = process_request)): fake_request(10.0) xa = pmnc.transaction.create() xa.jms_1.send(russian, JMSCorrelationID = russian, FOOBAR = "123") message_id = xa.execute()[0] request = loopback_queue.pop(10.0) assert request["message_id"] == message_id assert request["message_text"] == russian headers = request["headers"] assert headers["JMSCorrelationID"] == russian and headers["FOOBAR"] == "123"
def test_getlasterror(): fake_request(10.0) try: pmnc.transaction.mongodb_1.capped.drop() except ResourceError as e: if e.description == "ns not found": pass else: raise pmnc.transaction.mongodb_1.command("create", { "capped": True, "size": 10 }, create="capped") with expected(ResourceError, "10101.*remove from a capped collection.*"): pmnc.transaction.mongodb_1.capped.remove({"foo": "bar"})
def test_complex_search(): fake_request(10.0) rs = pmnc.transaction.mongodb_1.test.find( {"$query": { "k": { "$gt": 90 } }}, docs_to_return=-10) assert len(rs.documents) == 9 and rs.cursor_id == 0 rs = pmnc.transaction.mongodb_1.test.find( { "$where": BSON_JavaScript("function() { return this.k % 3 == 0; }") }, docs_to_return=-100) ds = [d["k"] for d in rs.documents] ds.sort() assert ds == list(range(0, 100, 3))
def test_mixed_access(): fake_request(10.0) pmnc.state.set("MIX-KEY", "VALUE") xa = pmnc.transaction.create() xa.state.get("MIX-KEY") assert xa.execute() == ("VALUE", ) xa = pmnc.transaction.create() xa.state.delete("MIX-KEY") assert xa.execute() == (None, ) assert pmnc.state.get("MIX-KEY") is None xa = pmnc.transaction.create() xa.state.set("MIX-KEY", "VALUE") assert xa.execute() == (None, ) assert pmnc.state.get("MIX-KEY") == "VALUE"
def test_log_truncation(): def current_logs(): return sorted([ int(s[4:]) for s in listdir(_module_state_dir(__name__)) if s.startswith("log.") ]) def grow_log(): for i in range(32): pmnc.state.set(str(randint(0, 127)), urandom(65536)) return current_logs() fake_request(60.0) logs_before = grow_log() logs_after = grow_log() while logs_before[0] == logs_after[0]: Timeout(1.0).wait() logs_after = grow_log() assert logs_before[0] < logs_after[0]
def test_transaction_isolation(): fake_request(10.0) tn = "table_{0:s}".format(random_string(8)) xa = pmnc.transaction.create() xa.oracle_1.execute("CREATE TABLE {0:s} (ID NUMBER(8) NOT NULL PRIMARY KEY)".format(tn)) xa.execute() xa = pmnc.transaction.create() xa.oracle_1.execute("INSERT INTO {0:s} (ID) VALUES ({{id}})".format(tn), "SELECT ID FROM {0:s}".format(tn), id = 1) xa.oracle_1.execute("INSERT INTO {0:s} (ID) VALUES ({{id}})".format(tn), "SELECT ID FROM {0:s}".format(tn), id = 2) assert xa.execute() == (([], [{ "ID": 1 }]), ([], [{ "ID": 2 }])) fake_request(5.0) xa = pmnc.transaction.create() xa.oracle_1.execute("INSERT INTO {0:s} (ID) VALUES ({{id}})".format(tn), id = 3) # causes a deadlock xa.oracle_1.execute("INSERT INTO {0:s} (ID) VALUES ({{id}})".format(tn), id = 3) with expected(Exception("request deadline")): xa.execute() fake_request(10.0) xa = pmnc.transaction.create() xa.oracle_1.execute("SELECT id FROM {0:s} ORDER BY id".format(tn), "DROP TABLE {0:s}".format(tn)) assert xa.execute()[0] == ([{ "ID": 1 }, { "ID": 2 }], [])
def test_errors(): fake_request(10.0) try: pmnc.transaction.postgresql_2.execute("SELECT 1/0") except SQLResourceError as e: assert e.code is None and e.state == "22012" assert e.recoverable and not e.terminal else: assert False t = "table_{0:s}".format(random_string(8)) fake_request(10.0) try: pmnc.transaction.postgresql_2.execute( "CREATE TABLE {t} (id int PRIMARY KEY)".format(t = t), "INSERT INTO {t} VALUES ({{id}})".format(t = t), "INSERT INTO {t} VALUES ({{id}})".format(t = t), id = 123) except SQLResourceError as e: assert e.code is None and e.state == "23505" assert e.recoverable and not e.terminal else: assert False fake_request(10.0) try: pmnc.transaction.postgresql_2.execute("DROP TABLE {t}".format(t = t)) except SQLResourceError as e: assert e.code is None and e.state == "42P01" assert e.recoverable and not e.terminal else: assert False
def test_failure(): fake_request(10.0) def execute(resource, *args, **kwargs): 1 / 0 # failure sequence (the instance is reused): begin_transaction, execute, rollback, disconnect xa = pmnc.transaction.create(biz="baz") xa.callable_1("abc", begin_transaction=begin_transaction, execute=execute, commit=commit, rollback=rollback, foo="bar").execute(1, 2, eee="fff") try: xa.execute() except ResourceError as e: assert by_regex("^(?:int )?division (?:or modulo )?by zero$")( str(e)) assert not e.recoverable and e.terminal else: assert False # now check the trace m, c, args, kwargs = q.pop(0.0) assert m == "begin_transaction" and c == 4 assert args == (xa._xid, ) assert kwargs == dict(transaction_options={"biz": "baz"}, source_module_name=__name__, resource_args=("abc", ), resource_kwargs={"foo": "bar"}) assert q.pop(1.0) == ( "rollback", 5 ) # rollback is not waited upon, therefore "rollback" may not appear in the queue immediately assert q.pop(1.0) == ("disconnect", 6) assert q.pop(1.0) is None
def _poller_proc(self, source_cage): while not current_thread().stopped(): try: # attach a new fake request to this thread so that # network access in RPC call times out properly fake_request(timeout=self._request_timeout, interface=self._name) pmnc.request.describe("polling cage {0:s}".format(source_cage)) # fetch a request from the remote cage # and extract the call information from it try: revrpc_request = self._poll(source_cage) except: pmnc.log.error(exc_string()) # failure to fetch a request current_thread().stopped( self._request_timeout) # results in idle delay continue if revrpc_request is None: continue elif current_thread().stopped(): break assert revrpc_request.pop( "target_cage") == __cage__, "expected call to this cage" assert revrpc_request.pop( "source_cage" ) == source_cage, "expected call from {0:s}".format( source_cage) self._process_request(source_cage, revrpc_request) except: pmnc.log.error(exc_string())
def test_success(): fake_request(10.0) def execute(resource, *args, **kwargs): resource._count += 1 resource._q.push(("execute", resource._count, args, kwargs)) return "ok" # success sequence: connect, begin_transaction, execute, commit (then the instance is put back to the pool) xa = pmnc.transaction.create(biz="baz") xa.callable_1("abc", begin_transaction=begin_transaction, execute=execute, commit=commit, rollback=rollback, foo="bar").execute(1, 2, eee="fff") assert xa.execute() == ("ok", ) # now check the trace assert q.pop(0.0) == ("connect", 0, { "param1": "value1", "param2": "value2" }) m, c, args, kwargs = q.pop(0.0) assert m == "begin_transaction" and c == 1 assert args == (xa._xid, ) assert kwargs == dict(transaction_options={"biz": "baz"}, source_module_name=__name__, resource_args=("abc", ), resource_kwargs={"foo": "bar"}) assert q.pop(0.0) == ("execute", 2, (1, 2), {"eee": "fff"}) assert q.pop(0.0) == ( "commit", 3 ) # commit is waited upon, therefore "commit" is in the queue assert q.pop(1.0) is None
def test_module_state(): fake_request(10.0) module_state_dir = _module_state_dir("foo") module_state = ModuleState(module_state_dir) try: # note that the first access to a queue or a database creates it, # therefore options such as re_len are required, but subsequent # accesses just open it and the options can be omitted assert not os_path.exists( os_path.join(module_state_dir, "state.queue")) queue = module_state.get_queue_db("state", re_len=100) assert os_path.exists(os_path.join(module_state_dir, "state.queue")) stat = queue.stat() assert "buckets" not in stat queue.append("foo") assert module_state.get_queue_db("state") is queue assert module_state.get_queue_db("state2") is not queue assert not os_path.exists( os_path.join(module_state_dir, "state.btree")) btree = module_state.get_btree_db("state") assert os_path.exists(os_path.join(module_state_dir, "state.btree")) stat = btree.stat() assert "buckets" not in stat with expected(bsddb.DBInvalidArgError): btree.append("foo") assert module_state.get_btree_db("state") is btree assert module_state.get_btree_db("state2") is not btree finally: module_state.close()
def test_performance(): fake_request(30.0) N = 250 table_name = random_name("t") qs = [ "CREATE TABLE {0:s} (id integer PRIMARY KEY)".format(table_name) ] ids = list(range(N)); shuffle(ids); ii = {} for i, id in enumerate(ids): n = format("id_{0:d}".format(i)) ii[n] = id qs.append("INSERT INTO {0:s} (id) VALUES ({{{1:s}}})".format(table_name, n)) qs.append("SELECT id FROM {0:s} ORDER BY newid()".format(table_name)) qs.append("DROP TABLE {0:s}".format(table_name)) start = time() rs = pmnc.transaction.sqlserver_1.execute(*qs, **ii)[-2] ids = list(map(lambda r: r["id"], rs)) stop = time() assert sorted(ids) == list(range(N)) pmnc.log("performance: {0:d} insert(s)/sec".format(int(N / (stop - start))))
def test_cross_db_deadlock(): fake_request(30.0) db1 = pmnc.state.get_database("db1") db2 = pmnc.state.get_queue("db2", re_len=6144) def f(txn): db1.put(b"key", b"value_1", txn) Timeout(3.0).wait() db2.append(pickle("item_1"), txn) def g(txn): db2.append(pickle("item_2"), txn) Timeout(3.0).wait() db1.put(b"key", b"value_2", txn) th_f = HeavyThread(target=lambda: pmnc.state.implicit_transaction(f)) th_g = HeavyThread(target=lambda: pmnc.state.implicit_transaction(g)) th_f.start() th_g.start() th_f.join() th_g.join() # now see what went through def fetch_result(txn): value = db1.get(b"key", None, txn) item1 = _pop(txn, db2, None, unpickle) item2 = _pop(txn, db2, None, unpickle) return value, item1, item2 value, item1, item2 = pmnc.state.implicit_transaction(fetch_result) assert value in (b"value_1", b"value_2") assert (item1, item2) in (("item_1", "item_2"), ("item_2", "item_1"))
def test_performance(): pmnc.log("begin performance test") # create table t = "table_{0:s}".format(random_string(8)) fake_request(10.0) pmnc.transaction.postgresql_1.execute( "CREATE TABLE {t} (id int PRIMARY KEY, key char(8) UNIQUE NOT NULL, " "pad varchar(200) NOT NULL)".format(t = t)) # populate table start = time() for i in range(10): fake_request(10.0) sqls, params = [], {} for j in range(100): id = i * 100 + j params["id{0:d}".format(id)] = id params["key{0:d}".format(id)] = str(id) params["pad{0:d}".format(id)] = random_string(200) sql = "INSERT INTO {t} VALUES ({{id{id}}}, {{key{id}}}, {{pad{id}}})".format(t = t, id = id) sqls.append(sql) pmnc.transaction.postgresql_1.execute(*sqls, **params) pmnc.log("{0:.01f} insert(s)/sec".format(1000 / (time() - start))) # query table stop = Timeout(10.0) count = InterlockedCounter() def th_proc(): while not stop.expired: fake_request(10.0) key = randint(0, 999) rs = pmnc.transaction.postgresql_1.execute( "SELECT id FROM {t} WHERE key = {{key}}".format(t = t), key = str(key)) assert rs[0][0]["id"] == key count.next() ths = [ Thread(target = th_proc) for i in range(5) ] for th in ths: th.start() for th in ths: th.join() pmnc.log("{0:.01f} select(s)/sec".format(count.next() / 10.0)) # drop table fake_request(10.0) pmnc.transaction.postgresql_1.execute("DROP TABLE {t}".format(t = t)) pmnc.log("end performance test")
def test_send_one(): def process_request(request, response): pass with active_interface("smpp_1", **interface_config(process_request = process_request)): sleep(3.0) # to allow connection to spin up fake_request(30.0) with expected(ResourceError, "^data_coding is not specified, provide short_message of type str$"): pmnc.transaction.smpp_1.submit_sm( dest_addr_ton = DEST_TON, dest_addr_npi = DEST_NPI, destination_addr = DEST_ADDR, short_message = b"test") with expected(ResourceError, "^data_coding is specified, provide short_message of type bytes$"): pmnc.transaction.smpp_1.submit_sm( dest_addr_ton = DEST_TON, dest_addr_npi = DEST_NPI, destination_addr = DEST_ADDR, short_message = "test", data_coding = 0x00) pmnc.transaction.smpp_1.submit_sm( dest_addr_ton = DEST_TON, dest_addr_npi = DEST_NPI, destination_addr = DEST_ADDR, short_message = b"test1", data_coding = 0x00) pmnc.transaction.smpp_1.submit_sm( dest_addr_ton = DEST_TON, dest_addr_npi = DEST_NPI, destination_addr = DEST_ADDR, short_message = "test2") # encoded to GSM7 pmnc.transaction.smpp_1.submit_sm( dest_addr_ton = DEST_TON, dest_addr_npi = DEST_NPI, destination_addr = DEST_ADDR, short_message = "@$\\") # encoded to GSM7 pmnc.transaction.smpp_1.submit_sm( dest_addr_ton = DEST_TON, dest_addr_npi = DEST_NPI, destination_addr = DEST_ADDR, short_message = russian) # encoded to UCS2
def th_proc(n): count = 0 def next_key(): nonlocal count count += 1 return "{0:d}_{1:d}".format(n, count) def next_value(): return urandom(randint(1, 2048)) while count < 10: fake_request(10.0) left_value = pop(lq) if left_value is not None: key, value = next_key(), left_value pmnc.state.implicit_transaction(_set, ldb, key, value) register(left_data, key, value) push(rq, left_value) push(rq, next_value()) right_value = pop(rq) if right_value is not None: key, value = next_key(), right_value pmnc.state.implicit_transaction(_set, rdb, key, value) register(right_data, key, value) push(lq, right_value) push(lq, next_value()) while True: fake_request(10.0) left_value = pop(lq) if left_value is not None: key, value = next_key(), left_value pmnc.state.implicit_transaction(_set, ldb, key, value) register(left_data, key, value) else: break while True: fake_request(10.0) right_value = pop(rq) if right_value is not None: key, value = next_key(), right_value pmnc.state.implicit_transaction(_set, rdb, key, value) register(right_data, key, value) else: break
def test_get_set_delete_deadlock(): fake_request(5.0) xa = pmnc.transaction.create() xa.state.set("key", "value1") xa.state.set("key", "value2") try: xa.execute() except (ResourceError, TransactionExecutionError): # depends on who times out first pass else: assert False fake_request(5.0) xa = pmnc.transaction.create() xa.state.set("key", "value") xa.execute() xa = pmnc.transaction.create() xa.state.delete("key") xa.state.get("key") try: xa.execute() except (ResourceError, TransactionExecutionError): # depends on who times out first pass else: assert False fake_request(5.0) xa = pmnc.transaction.create() xa.state.get("key") xa.state.get("key") assert xa.execute() == ("value", "value") xa = pmnc.transaction.create() xa.state.get("key") xa.state.set("key", "value2") try: xa.execute() except (ResourceError, TransactionExecutionError): # depends on who times out first pass else: assert False