def read(self, target_filename: str, *, remove: optional(bool) = None, frag_size: optional(int) = None) -> bytes: try: self._remove = remove or False self._target_filename, _ = self._normalize_filename(target_filename) file_size = os_path.getsize(self._target_filename) frag_size = frag_size or 65536 pmnc.log.info("reading {0:d} byte(s) from file {1:s}".\ format(file_size, self._target_filename)) try: data = BytesIO() with open(self._target_filename, "rb") as f: while not pmnc.request.expired: portion = f.read(frag_size) if not portion: break data.write(portion) else: raise Exception("request deadline reading data from file {0:s}".\ format(self._target_filename)) except: pmnc.log.info("reading {0:d} byte(s) from file {1:s} failed: {2:s}".\ format(file_size, self._target_filename, exc_string())) raise else: return data.getvalue() except: ResourceError.rethrow(recoverable = True) # no irreversible changes
def delete(self, key: str): try: self._txn = \ pmnc.state.explicit_transaction(self.source_module_name, self._txn, self._delete, key)[0] except: ResourceError.rethrow(recoverable = True, terminal = False)
def send(self, message_text: str, **kwargs) -> str: try: header_fields = " and {0:d} custom header field(s): {1:s}".\ format(len(kwargs), ", ".join(kwargs.keys())) \ if kwargs else "" correlation_id = kwargs.get("JMSCorrelationID") message_description = \ "JMS message{0:s} with {1:d} content byte(s){2:s}".\ format(" {0:s}".format(correlation_id) if correlation_id else "", len(message_text), header_fields) pmnc.log.info("sending {0:s}".format(message_description)) try: pkt = self._sync_adapter_command("SEND", XPmncMessageText = message_text, **kwargs) except: pmnc.log.warning("sending {0:s} failed: {1:s}".\ format(message_description, exc_string())) raise else: self._message_id = pkt["XPmncMessageID"] except: ResourceError.rethrow(recoverable = True) # no irreversible changes else: return self._message_id
def set(self, key: str, value): try: self._txn = \ pmnc.state.explicit_transaction(self.source_module_name, self._txn, self._set, key, value)[0] except: ResourceError.rethrow(recoverable = True, terminal = False)
def _map_reduce(self, collection: valid_collection, params: dict, **kwargs) -> MongoDB_Response: params_ = odict({"mapreduce": collection}) try: params_.update(map=params.pop("map")) params_.update(reduce=params.pop("reduce")) except KeyError: ResourceError.rethrow( description="map and reduce functions must be specified") query = params.pop("having", {}) params_.update(params) out = "{0:s}.mr_{1:s}".format(collection, b2a_hex(urandom(8)).decode("ascii")) params_["out"] = out rs = self._command("", "mapreduce", params_, **kwargs) try: counts = rs.documents[0]["counts"] if pmnc.log.debug: pmnc.log.debug( "-- MAP/REDUCE: IN={0[input]:d}, EMIT={0[emit]:d}, " "OUT={0[output]:d}".format(counts)) return self._find(out, query, docs_to_return=-counts["output"], exhaust=True) finally: self._drop(out)
def get(self, key: str, default=None): try: self._txn, result = pmnc.state.explicit_transaction( self.source_module_name, self._txn, self._get, key, default ) except: ResourceError.rethrow(recoverable=True, terminal=False) else: return result
def get(self, key: str, default = None): try: self._txn, result = \ pmnc.state.explicit_transaction(self.source_module_name, self._txn, self._get, key, default) except: ResourceError.rethrow(recoverable = True, terminal = False) else: return result
def _execute_sql(self, sql, params): try: param_list = ", ".join("@{0:s} = {1:s}".format(n, v) for n, v in params.items()) at_params = { n: "@{0:s}".format(n) for n in params.keys() } sql = sql.format(**at_params) except: ResourceError.rethrow(recoverable = True, terminal = False) cursor = self._connection.cursor() try: for n, v in params.items(): cursor.execute("SET @{0:s}={1:s}".format(n, v)) pmnc.log.info(">> {0:s}".format(sql)) if param_list: if pmnc.log.debug: pmnc.log.debug("-- {0:s} -- ({1:s})".format(sql, param_list)) records = [] try: cursor.execute(sql) rowcount = cursor.rowcount if rowcount >= 0: pmnc.log.info("<< OK, {0:d} record(s)".format(rowcount)) if rowcount > 0 and cursor.description: column_names = [ t[0] for t in cursor.description ] for record in cursor.fetchall(): records.append(dict(zip(column_names, record))) else: pmnc.log.info("<< OK") except MySQL_Error as e: code, message = e.args[0].args pmnc.log.warning("<< {0:s}{1:s} !! MySQL_Error(\"[{2:d}] {3:s}\") in {4:s}".\ format(sql, " -- ({0:s})".format(param_list) if param_list else "", code, message, trace_string())) SQLResourceError.rethrow(recoverable = True, code = code, description = message) # note that there is no state except Exception: pmnc.log.warning("<< {0:s}{1:s} !! {2:s}".\ format(sql, " -- ({0:s})".format(param_list) if param_list else "", exc_string())) ResourceError.rethrow(recoverable = True) else: return records finally: cursor.close()
def _execute_sql(self, sql, params): try: sql, params = self._convert_to_qmarks(sql, params) param_list = ", ".join(map(lambda x: isinstance(x, str) and "'{0:s}'".format(x) or str(x), params)) except: ResourceError.rethrow(recoverable = True, terminal = False) pmnc.log.info(">> {0:s}".format(sql)) if param_list: if pmnc.log.debug: pmnc.log.debug("-- {0:s} -- ({1:s})".format(sql, param_list)) records = [] try: self._cursor.execute(sql, params) rowcount = self._cursor.rowcount if rowcount >= 0: pmnc.log.info("<< OK, {0:d} record(s)".format(rowcount)) if self._cursor.description and rowcount > 0: column_names = [ t[0].decode("ascii") for t in self._cursor.description ] for record in self._cursor.fetchall(): records.append(dict(zip(column_names, record))) else: pmnc.log.info("<< OK") except ProgrammingError as e: try: level, state, message = map(self._decode_message, e.args) except: state, message = "PG800", str(e) state = state.upper() pmnc.log.warning("<< {0:s}{1:s} !! PostgreSQL_Error(\"[{2:s}] {3:s}\") in {4:s}".\ format(sql, " -- ({0:s})".format(param_list) if param_list else "", state, message, trace_string())) SQLResourceError.rethrow( state = state, description = message, # note that there is no code recoverable = True, terminal = state[:2] not in self._safe_states) except: pmnc.log.warning("<< {0:s}{1:s} !! {2:s}".\ format(sql, " -- ({0:s})".format(param_list) if param_list else "", exc_string())) ResourceError.rethrow(recoverable = True) else: return records
def _py_to_sql_int(self, v): if -2147483648 <= v <= 2147483647: return "adInteger", 4, v elif -9223372036854775808 <= v <= 9223372036854775807: return "adBigInt", 8, v else: raise ResourceError(description = "integer value too large", recoverable = True, terminal = False)
def _execute_sql(self, sql, params): try: sql, params = self._convert_bind_points(sql, params) param_list = ", ".join("{0:s}={1:s}".format(k, isinstance(v, str) and "'{0:s}'".format(v) or str(v)) for k, v in params.items()) except: ResourceError.rethrow(recoverable = True, terminal = False) pmnc.log.info(">> {0:s}".format(sql)) if param_list: if pmnc.log.debug: pmnc.log.debug("-- {0:s} -- ({1:s})".format(sql, param_list)) records = [] try: self._cursor.execute(sql, params) rowcount = self._cursor.rowcount if rowcount >= 0: pmnc.log.info("<< OK, {0:d} record(s)".format(rowcount)) if self._cursor.description and rowcount > 0: column_names = [ t.name for t in self._cursor.description ] for record in self._cursor.fetchall(): records.append(dict(zip(column_names, record))) else: pmnc.log.info("<< OK") except PGError as e: state, message = e.pgcode, " ".join(s.strip() for s in e.pgerror.split("\n")) pmnc.log.warning("<< {0:s}{1:s} !! {2:s}(\"{3:s}{4:s}\") in {5:s}".\ format(sql, " -- ({0:s})".format(param_list) if param_list else "", e.__class__.__name__, "[{0:s}] ".format(state) if state else "", message, trace_string())) SQLResourceError.rethrow( state = state, description = message, # note that there is no code recoverable = True, terminal = not state or state[:2] not in self._safe_states) except: pmnc.log.warning("<< {0:s}{1:s} !! {2:s}".\ format(sql, " -- ({0:s})".format(param_list) if param_list else "", exc_string())) ResourceError.rethrow(recoverable = True) else: return records
def write(self, target_filename: str, data_b: bytes, *, overwrite: optional(bool) = True): try: self._overwrite = overwrite or False self._target_filename, target_directory = self._normalize_filename(target_filename) # determine the location of temporary files # and create the temporary directory if necessary temp_directory = os_path.normpath(self._temp_directory or target_directory) if not os_path.isdir(temp_directory): self._create_directory(temp_directory) # write data to a temporary file and make sure # it has been persistently stored to disk h, self._temp_filename = mkstemp(dir = temp_directory, suffix = self._temp_suffix) pmnc.log.info("writing {0:d} byte(s) to a temporary file {1:s}".\ format(len(data_b), self._temp_filename)) try: with fdopen(h, "wb") as f: f.write(data_b) f.flush() fsync(h) except: pmnc.log.warning("writing {0:d} byte(s) to a temporary file {1:s} failed: " \ "{2:s}".format(len(data_b), self._temp_filename, exc_string())) raise # explicitly set permissions on the temporary file chmod(self._temp_filename, self._perm_mask) # this is a pessimistic safety check, if the target file already exists, # we are most likely unable to commit the transaction anyway, therefore # fail early to cause rollback if os_path.exists(self._target_filename) and not self._overwrite: raise Exception("file {0:s} already exists".format(self._target_filename)) except: ResourceError.rethrow(recoverable = True) # no irreversible changes
def _sync_request(self, rq: MongoDB_Request, check: optional(callable) = None) -> MongoDB_Response: pmnc.log.info(">> {0:s}".format(rq)) try: rs = self._connection.sync_request(rq, Timeout(pmnc.request.remain)) if rs.cursor_not_found: raise MongoDB_Error("cursor not found") if check: check(rs) except MongoDB_Error as e: pmnc.log.warning("<< {0:s} !! {1:s}".format(rq, exc_string())) ResourceError.rethrow(code=e.code, description=str(e), terminal=e.code is not None) except Exception as e: pmnc.log.warning("<< {0:s} !! {1:s}".format(rq, exc_string())) ResourceError.rethrow(description=str(e)) else: pmnc.log.info("<< OK, {0:s}".format(rs)) return rs
def __call__(self, *args): try: method, self._attrs = ".".join(self._attrs), [] request = dumps(args, methodname=method, encoding=self._request_encoding, allow_none=self._allow_none) request_description = "XMLRPC request {0:s} to {1:s}".\ format(method, self._http_resource.server_info) except: ResourceError.rethrow(recoverable=True) pmnc.log.info("sending {0:s}".format(request_description)) try: status_code, headers, content = \ self._http_resource.post(self._server_uri, request.encode(self._request_encoding), { "Content-Type": "text/xml" }) if status_code != 200: raise Exception( "HTTP request returned code {0:d}".format(status_code)) result = loads(content)[0][0] except Fault as e: pmnc.log.warning("{0:s} returned fault {1:d}: {2:s}".\ format(request_description, e.faultCode, e.faultString)) ResourceError.rethrow(code=e.faultCode, description=e.faultString, terminal=False) except: pmnc.log.warning("{0:s} failed: {1:s}".\ format(request_description, exc_string())) raise else: pmnc.log.info("XMLRPC request returned successfully") return result
def _async_request(self, rq: MongoDB_Request): pmnc.log.info(">> {0:s}".format(rq)) try: self._connection.async_request(rq, Timeout(pmnc.request.remain)) gle_rq = OP_QUERY("{0:s}.$cmd".format(self._database), {"getlasterror": 1}) gle_rs = self._connection.sync_request( gle_rq, Timeout(pmnc.request.remain)) MongoDB_Connection._command_check(gle_rs) d = gle_rs.documents[0] err = d.get("err") if err: raise MongoDB_Error(err, d.get("code")) except MongoDB_Error as e: pmnc.log.warning("<< {0:s} !! {1:s}".format(rq, exc_string())) ResourceError.rethrow(code=e.code, description=str(e), terminal=e.code is not None) except Exception as e: pmnc.log.warning("<< {0:s} !! {1:s}".format(rq, exc_string())) ResourceError.rethrow(description=str(e)) else: pmnc.log.info("<< OK")
def execute_reverse(target_cage: valid_cage_name, module: valid_module_name, method: valid_method_name, args: tuple, kwargs: dict): # wrap up an RPC call identical to how it's done in protocol_rpc.py request_dict = pmnc.request.to_dict() # remove request parameters that must not cross the RPC border request_dict["parameters"].pop("retry", None) # wrap all the call parameters in a plain dict request = dict(source_cage=__cage__, target_cage=target_cage, module=module, method=method, args=args, kwargs=kwargs, request=request_dict) request_description = "reverse RPC request {0:s}.{1:s} to {2:s}".\ format(module, method, target_cage) # create a one-time response queue just for this request rs_queue = InterlockedQueue() request_id = pmnc.request.unique_id with _rs_queues_lock: _rs_queues[request_id] = rs_queue # register the call as being active try: pmnc.log.info("sending {0:s}".format(request_description)) try: # enqueue the call and wait for response rq_queue = _get_rq_queue(target_cage) rq_queue.push((request_id, request)) response = pmnc.request.pop(rs_queue) if response is None: raise Exception("request deadline waiting for response") try: result = response["result"] except KeyError: raise RPCError(description=response["exception"], terminal=False) except RPCError as e: pmnc.log.warning("{0:s} returned error: {1:s}".\ format(request_description, e.description)) raise except: pmnc.log.warning("{0:s} failed: {1:s}".\ format(request_description, exc_string())) ResourceError.rethrow(recoverable=False) else: pmnc.log.info("reverse RPC request returned successfully") return result finally: with _rs_queues_lock: del _rs_queues[request_id] # unregister the call
def _execute_sql(self, sql, params): try: sql, params = self._convert_to_named(sql, params) param_list = ", ".join(map(lambda n_v: "{0:s} = {1:s}".\ format(n_v[0], isinstance(n_v[1], str) and "'{0:s}'".format(n_v[1]) or str(n_v[1])), params.items())) except: ResourceError.rethrow(recoverable = True, terminal = False) pmnc.log.info(">> {0:s}".format(sql)) if param_list: if pmnc.log.debug: pmnc.log.debug("-- {0:s} -- ({1:s})".format(sql, param_list)) try: cursor = self._connection.cursor() try: # avoid using floats with numbers cursor.numbersAsStrings = True # avoid truncation of timestamps and intervals param_sizes = {} for n, v in params.items(): if isinstance(v, datetime): param_sizes[n] = TIMESTAMP elif isinstance(v, timedelta): param_sizes[n] = INTERVAL if param_sizes: cursor.setinputsizes(**param_sizes) # execute the query, making stored procedure a special case execute = self._execute_parser.match(sql) if execute: proc_name = execute.group(1) result = self._connection.cursor() try: params.update(result = result) # providing out sys_refcursor parameter result cursor.callproc(proc_name, keywordParameters = params) finally: try: cursor.close() finally: cursor = result else: cursor.execute(sql, **params) # extract the result if cursor.description is not None: description = tuple(dict(name = t[0], type_name = t[1].__name__) for t in cursor.description) records = [ { field["name"]: self.cx_TYPE(field["type_name"], value) for field, value in zip(description, record) } for record in cursor ] else: records = [] # not a SELECT query records_affected = cursor.rowcount finally: cursor.close() if records_affected > 0: pmnc.log.info("<< OK, {0:d} record(s)".format(records_affected)) else: pmnc.log.info("<< OK") except Oracle_Error as e: e = e.args[0] if isinstance(e, _Oracle_Error): code, message = e.code, e.message else: code, message = -1, "cx_Oracle error: {0:s}".format(str(e)) pmnc.log.warning("<< {0:s}{1:s} !! Oracle_Error(\"{2:d}: {3:s}\") in {4:s}".\ format(sql, " -- ({0:s})".format(param_list) if param_list else "", code, message, trace_string())) SQLResourceError.rethrow( code = code, description = message, recoverable = True) # note that there is no state except: pmnc.log.warning("<< {0:s}{1:s} !! {2:s}".\ format(sql, " -- ({0:s})".format(param_list) if param_list else "", exc_string())) ResourceError.rethrow(recoverable = True) else: return records
def _py_to_sql_Decimal(self, v): return "adDecimal", self._decimal_bytes, \ self._ensure_decimal_in_range(v, lambda description: ResourceError(description = description, recoverable = True, terminal = False))
def _adodb_to_py_adDecimal(self, v): if isinstance(v, str): v = v.replace(",", ".") return self._ensure_decimal_in_range(Decimal(v), lambda description: ResourceError(description = description, recoverable = True, terminal = True))
def execute_reverse(target_cage: valid_cage_name, module: valid_module_name, method: valid_method_name, args: tuple, kwargs: dict): # wrap up an RPC call identical to how it's done in protocol_rpc.py request_dict = pmnc.request.to_dict() # remove request parameters that must not cross the RPC border request_dict["parameters"].pop("retry", None) # wrap all the call parameters in a plain dict request = dict(source_cage = __cage__, target_cage = target_cage, module = module, method = method, args = args, kwargs = kwargs, request = request_dict) request_description = "reverse RPC request {0:s}.{1:s} to {2:s}". \ format(module, method, target_cage) # create a one-time response queue just for this request rs_queue = InterlockedQueue() request_id = pmnc.request.unique_id with _rs_queues_lock: _rs_queues[request_id] = rs_queue # register the call as being active try: pmnc.log.info("sending {0:s}".format(request_description)) try: # enqueue the call and wait for response rq_queue = _get_rq_queue(target_cage) rq_queue.push((request_id, request)) response = pmnc.request.pop(rs_queue) if response is None: raise Exception("request deadline waiting for response") try: result = response["result"] except KeyError: raise RPCError(description = response["exception"], terminal = False) except RPCError as e: pmnc.log.warning("{0:s} returned error: {1:s}".\ format(request_description, e.description)) raise except: pmnc.log.warning("{0:s} failed: {1:s}".\ format(request_description, exc_string())) ResourceError.rethrow(recoverable = False) else: pmnc.log.info("reverse RPC request returned successfully") return result finally: with _rs_queues_lock: del _rs_queues[request_id] # unregister the call
def submit_sm(self, *, dest_addr_ton: byte, dest_addr_npi: byte, destination_addr: str, short_message: either(str, bytes), **kwargs) -> optional(str): try: destination_addr_b = destination_addr.encode("ascii", "replace") kwargs.setdefault("service_type", b"") kwargs.setdefault("source_addr_ton", self._source_addr_ton) kwargs.setdefault("source_addr_npi", self._source_addr_npi) kwargs.setdefault("source_addr", self._source_addr.encode("ascii", "replace")) kwargs.setdefault("esm_class", 0x00) kwargs.setdefault("protocol_id", 0x00) kwargs.setdefault("priority_flag", 0x00) kwargs.setdefault("schedule_delivery_time", b"") kwargs.setdefault("validity_period", b"") kwargs.setdefault("registered_delivery", 0x00) kwargs.setdefault("replace_if_present_flag", 0x00) kwargs.setdefault("sm_default_msg_id", 0x00) # if message text is provided as str, encode it to bytes using default # GSM7/UCS2 encoding, otherwise require the encoding to be specified if isinstance(short_message, str): if "data_coding" in kwargs: raise Exception("data_coding is specified, provide short_message of type bytes") data_coding, short_message_b = self._encode_message(short_message) kwargs["data_coding"] = data_coding elif "data_coding" in kwargs: short_message_b = short_message else: raise Exception("data_coding is not specified, provide short_message of type str") if len(short_message_b) > self._frag_size and self._frag_method: # the message needs to be fragmented bits_per_char = Resource._encoding_bits.get(kwargs["data_coding"], 8) if bits_per_char < 8 and not self._pack_7bit: bits_per_char = 8 # 7-bit characters are treated by some providers as octets, not septets msg_id = self._frag_count.next() # with 16 bits having a counter is better than picking at random if self._frag_method == "udh": # UDH is prepended to each fragment udh = kwargs.pop("udh", []) # sender could have provided his own UDH frags = self._frag_message(short_message_b, bits_per_char, self._frag_size, 7) # 7 bytes are reserved to UDH reqs = [ SubmitSmPDU.create(dest_addr_ton = dest_addr_ton, dest_addr_npi = dest_addr_npi, destination_addr = destination_addr_b, short_message = short_message_b, udh = udh + [ (0x08, pack("BBBB", msg_id >> 8, msg_id & 0xff, len(frags), i + 1)) ], **kwargs) for i, short_message_b in enumerate(frags) ] elif self._frag_method == "sar": # sar_... optional parameters are added to each fragment kwargs.setdefault("sar_msg_ref_num", msg_id) # sender could have provided his own value frags = self._frag_message(short_message_b, bits_per_char, self._frag_size) reqs = [ SubmitSmPDU.create(dest_addr_ton = dest_addr_ton, dest_addr_npi = dest_addr_npi, destination_addr = destination_addr_b, short_message = short_message_b, sar_segment_seqnum = i + 1, sar_total_segments = len(frags), **kwargs) for i, short_message_b in enumerate(frags) ] else: # no fragmentation required or possible reqs = [ SubmitSmPDU.create(dest_addr_ton = dest_addr_ton, dest_addr_npi = dest_addr_npi, destination_addr = destination_addr_b, short_message = short_message_b, **kwargs) ] except: ResourceError.rethrow(recoverable = True, terminal = False) try: pmnc.log.info("sending short message \"{0:s}\" for {1:s}".\ format(short_message, destination_addr)) try: # send all the fragments message_id = None for req in reqs: self._interface.send(req) # the responses are waited upon after all the requests have been sent if not self._asynchronous: for req in reqs: resp = _wait_response(req, pmnc.request.remain) resp_id = resp.message_id.value.decode("ascii", "replace") if resp_id and resp_id != message_id: message_id = resp_id # the last different message_id is returned except: pmnc.log.warning("sending short message \"{0:s}\" for {1:s} failed: {2:s}".\ format(short_message, destination_addr, exc_string())) raise else: pmnc.log.info("short message has been sent{0:s}".\ format(" as {0:s}".format(message_id) if message_id else "")) except: ResourceError.rethrow(recoverable = False, terminal = False) return message_id # possibly None, if sending asynchronously
def wu_participate(self, transaction_start, participant_index, resource_name, attrs, args, kwargs, res_args, res_kwargs): # see whether the request by which this transaction was created # has expired in the meantime, and if it has, simply bail out # because the transaction should have long been perished # no attempt to execute the request is taken and no result # is delivered, simply because the transaction is assumed # to already be aborted, nowhere to report the result if pmnc.request.expired: pmnc.log.error("execution of resource {0:s} in transaction " "{1:s} was late".format(resource_name, self)) return try: pmnc.log.debug("resource {0:s} joins transaction {1:s}".\ format(resource_name, self)) resource_instance = None # no instance has been allocated yet resource_in_transaction = False # no transaction has been started on the instance resource_failed = True # (preventive) request execution has been a failure while True: # breaks when the result is obtained, either value or exception # any failure prior to actual resource allocation results # in a recoverable ResourceError, pointlessly terminal try: # the pending interval is measured from the beginning of the # transaction, not from the beginning of the request pending_ms = int((time() - transaction_start) * 1000) pmnc.performance.sample("resource.{0:s}.pending_time".\ format(resource_name), pending_ms) # allocate a resource instance from a specific resource pool resource_pool = pmnc.shared_pools.get_resource_pool(resource_name) resource_instance = resource_pool.allocate() except: # tested result = ResourceError.snap_exception( participant_index = participant_index, recoverable = True, terminal = True) # but not really terminal, break # while True # no instance to terminate # some resource instance has been allocated, beginning a transaction, # a failure would result in a ResourceError, recoverable yet terminal # unless explicitly specified otherwise try: # see if a transaction should be started in as much time as the request has left if pmnc.request.remain < resource_instance.min_time: raise ResourceError(description = "transaction {0:s} is declined by resource instance " # tested "{1:s}".format(self, resource_instance.name), recoverable = True, terminal = False) # the instance stays alive pmnc.log.debug("resource instance {0:s} is used in transaction {1:s}, {2:s}".\ format(resource_instance.name, self, self._resource_ttl(resource_instance))) # begin a new transaction, this is presumably reversible operation resource_instance.begin_transaction(self._xid, source_module_name = self._source_module_name, transaction_options = self._options, resource_args = res_args, resource_kwargs = res_kwargs) except ResourceError as e: result = self._apply_error(participant_index, resource_instance, e) break # while True except: # tested result = ResourceError.snap_exception( participant_index = participant_index, recoverable = True, terminal = True) resource_instance.expire() break # while True else: resource_in_transaction = True # resource instance is now in transaction, executing the request, # a failure would result in a ResourceError, unrecoverable and # terminal unless explicitly specified otherwise try: # replay attribute accesses to obtain the actual target method target_method = resource_instance for attr in attrs: target_method = getattr(target_method, attr) # execute the request, registering the execution time with pmnc.performance.timing("resource.{0:s}.processing_time".format(resource_name)): result = target_method(*args, **kwargs) except ResourceError as e: result = self._apply_error(participant_index, resource_instance, e) break # while True except Exception: # tested result = ResourceError.snap_exception( participant_index = participant_index, recoverable = False, terminal = True) resource_instance.expire() break # while True else: resource_instance.reset_idle_timeout() resource_failed = False break # while True # we got an intermediate result, possibly an exception try: self._results.push((participant_index, result)) # deliver the result to the pending transaction # register the actual result of this participant pmnc.performance.event("resource.{0:s}.transaction_rate.{1:s}".\ format(resource_name, resource_failed and "failure" or "success")) if not resource_in_transaction: # as we couldn't begin a transaction, return "failure" # we are not interested in the decision pmnc.log.debug("resource instance {0:s} is waiting for decision in " "transaction {1:s}".format(resource_instance.name, self)) # figure out whether the resource has to commit or rollback commit_transaction = False if pmnc.request.wait(self._decision): # wait for transaction's decision if self._commit.is_set(): if not resource_failed: commit_transaction = True pmnc.log.debug("resource instance {0:s} decided to commit in transaction " "{1:s}".format(resource_instance.name, self)) else: pmnc.log.warning("resource instance {0:s} had to rollback despite decision to commit " "in transaction {1:s}".format(resource_instance.name, self)) else: pmnc.log.debug("resource instance {0:s} decided to rollback in transaction " "{1:s}".format(resource_instance.name, self)) else: pmnc.log.warning("resource instance {0:s} had to abandon waiting for decision and " "rollback in transaction {1:s}".format(resource_instance.name, self)) # complete the transaction and return the final outcome if commit_transaction: try: resource_instance.commit() except: pmnc.log.error("resource instance {0:s} failed to commit in transaction {1:s}: " "{2:s}".format(resource_instance.name, self, exc_string())) # this is a severe problem resource_instance.expire() return "failure" else: pmnc.log.debug("resource instance {0:s} committed in transaction " "{1:s}".format(resource_instance.name, self)) return "commit" else: try: resource_instance.rollback() except: pmnc.log.warning("resource instance {0:s} failed to rollback in transaction {1:s}: " "{2:s}".format(resource_instance.name, self, exc_string())) # this is not a big deal resource_instance.expire() return "failure" else: pmnc.log.debug("resource instance {0:s} rolled back in transaction " "{1:s}".format(resource_instance.name, self)) return "rollback" finally: if resource_instance: pmnc.log.debug("resource instance {0:s} is being released, {1:s}".\ format(resource_instance.name, self._resource_ttl(resource_instance))) resource_pool.release(resource_instance) except: pmnc.log.error(exc_string()) # this should not normally happen, but do raise # not allow such exception to be silenced
def set(self, key: str, value): try: self._txn = pmnc.state.explicit_transaction(self.source_module_name, self._txn, self._set, key, value)[0] except: ResourceError.rethrow(recoverable=True, terminal=False)
def delete(self, key: str): try: self._txn = pmnc.state.explicit_transaction(self.source_module_name, self._txn, self._delete, key)[0] except: ResourceError.rethrow(recoverable=True, terminal=False)
def _execute_sql(self, sql, params): try: sql, params = self._convert_to_qmarks(sql, params) param_list = ", ".join(map(lambda t_s_v: "{0:s}({1:s})".\ format(t_s_v[0], isinstance(t_s_v[2], str) and "'{0:s}'".format(t_s_v[2]) or str(t_s_v[2])), params)) except: ResourceError.rethrow(recoverable = True, terminal = False) pmnc.log.info(">> {0:s}".format(sql)) if param_list: if pmnc.log.debug: pmnc.log.debug("-- {0:s} -- ({1:s})".format(sql, param_list)) records = [] try: command = com_client.Dispatch("ADODB.Command") command.CommandText = sql command.CommandType = 1 # adCmdText for i, (param_type, param_size, param_value) in enumerate(params): param = command.CreateParameter(None, ADODataTypes[param_type], 1, # adParamInput param_size, param_value) if param_type in ("adDecimal", "adNumeric"): param.Precision = self.precision param.NumericScale = self.scale command.Parameters.Append(param) command.ActiveConnection = self._connection command.Prepared = True recordset = command.Execute()[0] if recordset.State == 1: # adStateOpen try: while not recordset.EOF: records.append({ field.Name: TypeValueWrapper((field.Type, field.Value)) for field in recordset.Fields }) recordset.MoveNext() finally: recordset.Close() pmnc.log.info("<< OK, {0:d} record(s)".format(len(records))) else: pmnc.log.info("<< OK") except com_error: tb, code, state, description = self._extract_adodb_error() state_brace = " [{0:s}]".format(state) if state is not None else "" pmnc.log.warning("<< {0:s}{1:s} !! ADODB_Error(\"{2:d}:{3:s} {4:s}\") in {5:s}".\ format(sql, " -- ({0:s})".format(param_list) if param_list else "", code, state_brace, description, trace_string())) SQLResourceError.rethrow( code = code, state = state, description = description, recoverable = True, terminal = state[:2] not in self._safe_states) except: pmnc.log.warning("<< {0:s}{1:s} !! {2:s}".\ format(sql, " -- ({0:s})".format(param_list) if param_list else "", exc_string())) ResourceError.rethrow(recoverable = True) else: return records