def validate_client_submit_response(json): if u'error' in json: error = Error._parse(json) raise error if u'header' in json: header = Header._parse(json[u'header']) else: header = None responses = [] for r in json.get(u'responses', []): if len(r.keys()) != 1: raise Exception( 'bogus transaction response (multiple response tags in item): {}' .format(json)) first = list(r.keys())[0] if first == u'response_put': re = Revision._parse(r[u'response_put']) elif first == u'response_delete_range': re = Deleted._parse(r[u'response_delete_range']) elif first == u'response_range': re = Range._parse(r[u'response_range']) else: raise Exception( 'response item "{}" bogus or not implemented'.format(first)) responses.append(re) return header, responses
async def delete(self, key, return_previous=None, timeout=None): assembler = commons.DeleteRequestAssembler(self._url, key, return_previous) obj = await self._post(assembler.url, assembler.data, timeout) return Deleted._parse(obj)
def delete(self, key, return_previous=None, timeout=None): """ Delete value(s) from etcd. :param key: key is the first key to delete in the range. :type key: bytes or instance of :class:`txaioetcd.KeySet` :param return_previous: If enabled, return the deleted key-value pairs :type return_previous: bool or None :param timeout: Request timeout in seconds. :type timeout: int :returns: Deletion info :rtype: instance of :class:`txaioetcd.Deleted` """ assembler = commons.DeleteRequestAssembler(self._url, key, return_previous) obj = yield self._post(assembler.url, assembler.data, timeout) deleted = Deleted._parse(obj) returnValue(deleted)
def submit(self, txn, timeout=None): """ Submit a transaction. Processes multiple requests in a single transaction. A transaction increments the revision of the key-value store and generates events with the same revision for every completed request. It is not allowed to modify the same key several times within one transaction. From google paxosdb paper: Our implementation hinges around a powerful primitive which we call MultiOp. All other database operations except for iteration are implemented as a single call to MultiOp. A MultiOp is applied atomically and consists of three components: 1. A list of tests called guard. Each test in guard checks a single entry in the database. It may check for the absence or presence of a value, or compare with a given value. Two different tests in the guard may apply to the same or different entries in the database. All tests in the guard are applied and MultiOp returns the results. If all tests are true, MultiOp executes t op (see item 2 below), otherwise it executes f op (see item 3 below). 2. A list of database operations called t op. Each operation in the list is either an insert, delete, or lookup operation, and applies to a single database entry. Two different operations in the list may apply to the same or different entries in the database. These operations are executed if guard evaluates to true. 3. A list of database operations called f op. Like t op, but executed if guard evaluates to false. :param txn: The transaction to submit. :type txn: instance of :class:`txaioetcd.Transaction` :param timeout: Request timeout in seconds. :type timeout: int :returns: An instance of :class:`txaioetcd.Success` or an exception of :class:`txioetcd.Failed` or :class:`txaioetcd.Error` :rtype: instance of :class:`txaioetcd.Success`, :class:`txaioetcd.Failed` or :class:`txaioetcd.Error` """ url = u'{}/v3alpha/kv/txn'.format(self._url).encode() obj = txn._marshal() data = json.dumps(obj).encode('utf8') response = yield treq.post(url, data, headers=self._REQ_HEADERS, timeout=(timeout or self._timeout)) obj = yield treq.json_content(response) if u'error' in obj: error = Error._parse(obj) raise error if u'header' in obj: header = Header._parse(obj[u'header']) else: header = None responses = [] for r in obj.get(u'responses', []): if len(r.keys()) != 1: raise Exception('bogus transaction response (multiple response tags in item): {}'.format(obj)) first = list(r.keys())[0] if first == u'response_put': re = Revision._parse(r[u'response_put']) elif first == u'response_delete_range': re = Deleted._parse(r[u'response_delete_range']) elif first == u'response_range': re = Range._parse(r[u'response_range']) else: raise Exception('response item "{}" bogus or not implemented'.format(first)) responses.append(re) if obj.get(u'succeeded', False): returnValue(Success(header, responses)) else: raise Failed(header, responses)
def delete(self, key, return_previous=None, timeout=None): """ Delete value(s) from etcd. :param key: key is the first key to delete in the range. :type key: bytes or instance of :class:`txaioetcd.KeySet` :param return_previous: If enabled, return the deleted key-value pairs :type return_previous: bool or None :param timeout: Request timeout in seconds. :type timeout: int :returns: Deletion info :rtype: instance of :class:`txaioetcd.Deleted` """ if type(key) == six.binary_type: key = KeySet(key) elif isinstance(key, KeySet): pass else: raise TypeError('key must either be bytes or a KeySet object, not {}'.format(type(key))) if return_previous is not None and type(return_previous) != bool: raise TypeError('return_previous must be bool, not {}'.format(type(return_previous))) if key.type == KeySet._SINGLE: range_end = None elif key.type == KeySet._PREFIX: range_end = _increment_last_byte(key.key) elif key.type == KeySet._RANGE: range_end = key.range_end else: raise Exception('logic error') url = u'{}/v3alpha/kv/deleterange'.format(self._url).encode() obj = { u'key': base64.b64encode(key.key).decode(), } if range_end: # range_end is the key following the last key to delete # for the range [key, range_end). # If range_end is not given, the range is defined to contain only # the key argument. # If range_end is one bit larger than the given key, then the range # is all keys with the prefix (the given key). # If range_end is '\\0', the range is all keys greater # than or equal to the key argument. # obj[u'range_end'] = base64.b64encode(range_end).decode() if return_previous: # If prev_kv is set, etcd gets the previous key-value pairs # before deleting it. # The previous key-value pairs will be returned in the # delete response. # obj[u'prev_kv'] = True data = json.dumps(obj).encode('utf8') response = yield treq.post(url, data, headers=self._REQ_HEADERS, timeout=(timeout or self._timeout)) obj = yield treq.json_content(response) deleted = Deleted._parse(obj) returnValue(deleted)