Пример #1
0
  def record_reads(self, app, txid, group_keys):
    """ Keep track of which entity groups were read in a transaction.

    Args:
      app: A string specifying an application ID.
      txid: An integer specifying a transaction ID.
      group_keys: An iterable containing Reference objects.
    """
    batch = BatchStatement(consistency_level=ConsistencyLevel.QUORUM,
                           retry_policy=BASIC_RETRIES)
    insert = self.session.prepare("""
      INSERT INTO transactions (txid_hash, operation, namespace, path)
      VALUES (?, ?, ?, ?)
      USING TTL {ttl}
    """.format(ttl=dbconstants.MAX_TX_DURATION * 2))

    for group_key in group_keys:
      if not isinstance(group_key, entity_pb.Reference):
        group_key = entity_pb.Reference(group_key)

      args = (tx_partition(app, txid),
              TxnActions.GET,
              group_key.name_space(),
              bytearray(group_key.path().Encode()))
      batch.add(insert, args)

    try:
      yield self.tornado_cassandra.execute(batch)
    except dbconstants.TRANSIENT_CASSANDRA_ERRORS:
      message = 'Exception while recording reads in a transaction'
      logger.exception(message)
      raise AppScaleDBConnectionError(message)
Пример #2
0
  def delete_entities_tx(self, app, txid, entity_keys):
    """ Update transaction metadata with new delete operations.

    Args:
      app: A string containing an application ID.
      txid: An integer specifying the transaction ID.
      entity_keys: A list of entity keys that will be deleted upon commit.
    """
    batch = BatchStatement(consistency_level=ConsistencyLevel.QUORUM,
                           retry_policy=BASIC_RETRIES)
    insert = self.session.prepare("""
      INSERT INTO transactions (txid_hash, operation, namespace, path, entity)
      VALUES (?, ?, ?, ?, ?)
      USING TTL {ttl}
    """.format(ttl=dbconstants.MAX_TX_DURATION * 2))

    for key in entity_keys:
      # The None value overwrites previous puts.
      args = (tx_partition(app, txid),
              TxnActions.MUTATE,
              key.name_space(),
              bytearray(key.path().Encode()),
              None)
      batch.add(insert, args)

    try:
      yield self.tornado_cassandra.execute(batch)
    except dbconstants.TRANSIENT_CASSANDRA_ERRORS:
      message = 'Exception while deleting entities in a transaction'
      logger.exception(message)
      raise AppScaleDBConnectionError(message)
Пример #3
0
  def start(self, retries=5):
    """ Mark the batch as being in progress.

    Args:
      retries: The number of times to retry after failures.
    Raises:
      FailedBatch if the batch cannot be marked as being started.
    """
    if retries < 0:
      raise FailedBatch('Retries exhausted while starting batch')

    insert = SimpleStatement("""
      INSERT INTO batch_status (txid_hash, applied, op_id)
      VALUES (%(txid_hash)s, False, %(op_id)s)
      IF NOT EXISTS
    """, retry_policy=NO_RETRIES)
    parameters = {'txid_hash': tx_partition(self.project, self.txid),
                  'op_id': self.op_id}

    try:
      result = self.session.execute(insert, parameters)
    except TRANSIENT_CASSANDRA_ERRORS:
      return self.start(retries=retries - 1)

    if result.was_applied:
      return

    # Make sure this process was responsible for the insert.
    try:
      self.is_applied()
    except (BatchNotOwned, TRANSIENT_CASSANDRA_ERRORS) as batch_failure:
      raise FailedBatch(str(batch_failure))
    except BatchNotFound:
      return self.start(retries=retries - 1)
Пример #4
0
  def cleanup(self, retries=5):
    """ Clean up the batch status entry.

    Args:
      retries: The number of times to retry after failures.
    Raises:
      FailedBatch if the batch cannot be marked as applied.
    """
    if retries < 0:
      raise FailedBatch('Retries exhausted while cleaning up batch')

    clear_status = SimpleStatement("""
      DELETE FROM batch_status
      WHERE txid_hash = %(txid_hash)s
      IF op_id = %(op_id)s
    """, retry_policy=NO_RETRIES)
    parameters = {'txid_hash': tx_partition(self.project, self.txid),
                  'op_id': self.op_id}

    try:
      result = yield self.tornado_cassandra.execute(clear_status, parameters)
    except TRANSIENT_CASSANDRA_ERRORS:
      yield self.cleanup(retries=retries-1)
      return

    if not result.was_applied:
      raise FailedBatch(
        'Unable to clean up batch for {}:{}'.format(self.project, self.txid))
Пример #5
0
  def claim(self):
    """ Claim a batch so that other processes don't work on it.

    Raises:
      FailedBatch if the batch cannot be claimed.
    """
    try:
      if self.is_applied():
        self.applied = True
    except TRANSIENT_CASSANDRA_ERRORS as error:
      raise FailedBatch(str(error))
    except BatchNotOwned:
      # This process does not own the batch yet.
      pass
    except BatchNotFound:
      # Make sure another process doesn't try to start.
      return self.start()

    update_id = SimpleStatement("""
      UPDATE batch_status
      SET op_id = %(new_op_id)s
      WHERE txid_hash = %(txid_hash)s
      IF op_id = %(old_op_id)s
    """, retry_policy=NO_RETRIES)
    parameters = {'txid_hash': tx_partition(self.project, self.txid),
                  'new_op_id': self.op_id, 'old_op_id': self.read_op_id}

    try:
      result = self.session.execute(update_id, parameters)
      assert result.was_applied
    except (TRANSIENT_CASSANDRA_ERRORS, AssertionError):
      raise FailedBatch('Unable to claim batch')
Пример #6
0
  def set_applied(self, retries=5):
    """ Mark the batch as being applied.

    Args:
      retries: The number of times to retry after failures.
    Raises:
      FailedBatch if the batch cannot be marked as applied.
    """
    if retries < 0:
      raise FailedBatch('Retries exhausted while updating batch')

    update_status = SimpleStatement("""
      UPDATE batch_status
      SET applied = True
      WHERE txid_hash = %(txid_hash)s
      IF op_id = %(op_id)s
    """, retry_policy=NO_RETRIES)
    parameters = {'txid_hash': tx_partition(self.project, self.txid),
                  'op_id': self.op_id}

    try:
      result = self.session.execute(update_status, parameters)
      if result.was_applied:
        self.applied = True
        return
    except TRANSIENT_CASSANDRA_ERRORS:
      pass  # Application is confirmed below.

    try:
      if self.is_applied():
        self.applied = True
        return
      return self.set_applied(retries=retries - 1)
    except (BatchNotFound, BatchNotOwned, TRANSIENT_CASSANDRA_ERRORS) as error:
      raise FailedBatch(str(error))
Пример #7
0
    def delete_entities_tx(self, app, txid, entity_keys):
        """ Update transaction metadata with new delete operations.

    Args:
      app: A string containing an application ID.
      txid: An integer specifying the transaction ID.
      entity_keys: A list of entity keys that will be deleted upon commit.
    """
        batch = BatchStatement(consistency_level=ConsistencyLevel.QUORUM,
                               retry_policy=BASIC_RETRIES)
        insert = self.session.prepare("""
      INSERT INTO transactions (txid_hash, operation, namespace, path, entity)
      VALUES (?, ?, ?, ?, ?)
      USING TTL {ttl}
    """.format(ttl=dbconstants.MAX_TX_DURATION * 2))

        for key in entity_keys:
            # The None value overwrites previous puts.
            args = (tx_partition(app,
                                 txid), TxnActions.MUTATE, key.name_space(),
                    bytearray(key.path().Encode()), None)
            batch.add(insert, args)

        try:
            yield self.tornado_cassandra.execute(batch)
        except dbconstants.TRANSIENT_CASSANDRA_ERRORS:
            message = 'Exception while deleting entities in a transaction'
            logger.exception(message)
            raise AppScaleDBConnectionError(message)
Пример #8
0
    def cleanup(self, retries=5):
        """ Clean up the batch status entry.

    Args:
      retries: The number of times to retry after failures.
    Raises:
      FailedBatch if the batch cannot be marked as applied.
    """
        if retries < 0:
            raise FailedBatch('Retries exhausted while cleaning up batch')

        clear_status = SimpleStatement("""
      DELETE FROM batch_status
      WHERE txid_hash = %(txid_hash)s
      IF op_id = %(op_id)s
    """,
                                       retry_policy=NO_RETRIES)
        parameters = {
            'txid_hash': tx_partition(self.project, self.txid),
            'op_id': self.op_id
        }

        try:
            result = self.session.execute(clear_status, parameters)
        except TRANSIENT_CASSANDRA_ERRORS:
            return self.cleanup(retries=retries - 1)

        if not result.was_applied:
            raise FailedBatch('Unable to clean up batch for {}:{}'.format(
                self.project, self.txid))
Пример #9
0
    def record_reads(self, app, txid, group_keys):
        """ Keep track of which entity groups were read in a transaction.

    Args:
      app: A string specifying an application ID.
      txid: An integer specifying a transaction ID.
      group_keys: An iterable containing Reference objects.
    """
        batch = BatchStatement(consistency_level=ConsistencyLevel.QUORUM,
                               retry_policy=BASIC_RETRIES)
        insert = self.session.prepare("""
      INSERT INTO transactions (txid_hash, operation, namespace, path)
      VALUES (?, ?, ?, ?)
      USING TTL {ttl}
    """.format(ttl=dbconstants.MAX_TX_DURATION * 2))

        for group_key in group_keys:
            if not isinstance(group_key, entity_pb.Reference):
                group_key = entity_pb.Reference(group_key)

            args = (tx_partition(app,
                                 txid), TxnActions.GET, group_key.name_space(),
                    bytearray(group_key.path().Encode()))
            batch.add(insert, args)

        try:
            yield self.tornado_cassandra.execute(batch)
        except dbconstants.TRANSIENT_CASSANDRA_ERRORS:
            message = 'Exception while recording reads in a transaction'
            logger.exception(message)
            raise AppScaleDBConnectionError(message)
Пример #10
0
  def cleanup(self, txid):
    """ Cleans up the metadata from the finished batch.

    Args:
      txid: An integer specifying a transaction ID.
    """
    txid_hash = tx_partition(self.project_id, txid)
    yield self._delete_mutations(txid)
    yield self._delete_status(txid_hash)
Пример #11
0
    def cleanup(self, txid):
        """ Cleans up the metadata from the finished batch.

    Args:
      txid: An integer specifying a transaction ID.
    """
        txid_hash = tx_partition(self.project_id, txid)
        yield self._delete_mutations(txid)
        yield self._delete_status(txid_hash)
Пример #12
0
  def get_transaction_metadata(self, app, txid):
    """ Fetch transaction state.

    Args:
      app: A string specifying an application ID.
      txid: An integer specifying a transaction ID.
    Returns:
      A dictionary containing transaction state.
    """
    select = (
      'SELECT namespace, operation, path, start_time, is_xg, in_progress, '
      '       entity, task '
      'FROM transactions '
      'WHERE txid_hash = %(txid_hash)s '
    )
    parameters = {'txid_hash': tx_partition(app, txid)}
    try:
      results = yield self.tornado_cassandra.execute(select, parameters)
    except dbconstants.TRANSIENT_CASSANDRA_ERRORS:
      message = 'Exception while inserting entities in a transaction'
      logger.exception(message)
      raise AppScaleDBConnectionError(message)

    metadata = {'puts': {}, 'deletes': [], 'tasks': [], 'reads': set()}
    for result in results:
      if result.operation == TxnActions.START:
        metadata['start'] = result.start_time
        metadata['is_xg'] = result.is_xg
        metadata['in_progress'] = set()
        if metadata['in_progress'] is not None:
          metadata['in_progress'] = set(
            struct.unpack('q' * int(len(result.in_progress) / 8),
                          result.in_progress))
      if result.operation == TxnActions.MUTATE:
        key = create_key(app, result.namespace, result.path)
        if result.entity is None:
          metadata['deletes'].append(key)
        else:
          metadata['puts'][key.Encode()] = result.entity
      if result.operation == TxnActions.GET:
        group_key = create_key(app, result.namespace, result.path)
        metadata['reads'].add(group_key.Encode())
      if result.operation == TxnActions.ENQUEUE_TASK:
        service_id, version_id, task_pb = result.task.split('_', 2)
        task_metadata = {
          'service_id': service_id,
          'version_id': version_id,
          'task': taskqueue_service_pb.TaskQueueAddRequest(task_pb)}
        metadata['tasks'].append(task_metadata)
    raise gen.Return(metadata)
Пример #13
0
    def get_transaction_metadata(self, app, txid):
        """ Fetch transaction state.

    Args:
      app: A string specifying an application ID.
      txid: An integer specifying a transaction ID.
    Returns:
      A dictionary containing transaction state.
    """
        select = (
            'SELECT namespace, operation, path, start_time, is_xg, in_progress, '
            '       entity, task '
            'FROM transactions '
            'WHERE txid_hash = %(txid_hash)s ')
        parameters = {'txid_hash': tx_partition(app, txid)}
        try:
            results = yield self.tornado_cassandra.execute(select, parameters)
        except dbconstants.TRANSIENT_CASSANDRA_ERRORS:
            message = 'Exception while inserting entities in a transaction'
            logger.exception(message)
            raise AppScaleDBConnectionError(message)

        metadata = {'puts': {}, 'deletes': [], 'tasks': [], 'reads': set()}
        for result in results:
            if result.operation == TxnActions.START:
                metadata['start'] = result.start_time
                metadata['is_xg'] = result.is_xg
                metadata['in_progress'] = set()
                if metadata['in_progress'] is not None:
                    metadata['in_progress'] = set(
                        struct.unpack('q' * int(len(result.in_progress) / 8),
                                      result.in_progress))
            if result.operation == TxnActions.MUTATE:
                key = create_key(app, result.namespace, result.path)
                if result.entity is None:
                    metadata['deletes'].append(key)
                else:
                    metadata['puts'][key.Encode()] = result.entity
            if result.operation == TxnActions.GET:
                group_key = create_key(app, result.namespace, result.path)
                metadata['reads'].add(group_key.Encode())
            if result.operation == TxnActions.ENQUEUE_TASK:
                service_id, version_id, task_pb = result.task.split('_', 2)
                task_metadata = {
                    'service_id': service_id,
                    'version_id': version_id,
                    'task': taskqueue_service_pb.TaskQueueAddRequest(task_pb)
                }
                metadata['tasks'].append(task_metadata)
        raise gen.Return(metadata)
Пример #14
0
    def is_applied(self, retries=5):
        """ Fetch the status of the batch.

    Args:
      retries: The number of times to retry after failures.
    Returns:
      A boolean indicating whether or not the batch has been applied.
    Raises:
      BatchNotFound if the batch cannot be found.
      BatchNotOwned if a different process owns the batch.
    """
        if self.applied:
            raise gen.Return(True)

        get_status = """
      SELECT applied, op_id FROM batch_status
      WHERE txid_hash = %(txid_hash)s
    """
        query = SimpleStatement(get_status,
                                retry_policy=BASIC_RETRIES,
                                consistency_level=ConsistencyLevel.SERIAL)
        parameters = {'txid_hash': tx_partition(self.project, self.txid)}

        try:
            results = yield self.tornado_cassandra.execute(
                query, parameters=parameters)
            result = results[0]
            if result.op_id != self.op_id:
                self.read_op_id = result.op_id
                raise BatchNotOwned('{} does not match {}'.format(
                    self.op_id, result.op_id))
            raise gen.Return(result.applied)
        except TRANSIENT_CASSANDRA_ERRORS:
            retries_left = retries - 1
            if retries_left < 0:
                raise

            logger.debug('Unable to read batch status. Retrying.')
            is_applied = yield self.is_applied(retries=retries_left)
            raise gen.Return(is_applied)
        except IndexError:
            raise BatchNotFound('Batch for {}:{} not found'.format(
                self.project, self.txid))
Пример #15
0
  def is_applied(self, retries=5):
    """ Fetch the status of the batch.

    Args:
      retries: The number of times to retry after failures.
    Returns:
      A boolean indicating whether or not the batch has been applied.
    Raises:
      BatchNotFound if the batch cannot be found.
      BatchNotOwned if a different process owns the batch.
    """
    if self.applied:
      raise gen.Return(True)

    get_status = """
      SELECT applied, op_id FROM batch_status
      WHERE txid_hash = %(txid_hash)s
    """
    query = SimpleStatement(get_status, retry_policy=BASIC_RETRIES,
                            consistency_level=ConsistencyLevel.SERIAL)
    parameters = {'txid_hash': tx_partition(self.project, self.txid)}

    try:
      results = yield self.tornado_cassandra.execute(
        query, parameters=parameters)
      result = results[0]
      if result.op_id != self.op_id:
        self.read_op_id = result.op_id
        raise BatchNotOwned(
          '{} does not match {}'.format(self.op_id, result.op_id))
      raise gen.Return(result.applied)
    except TRANSIENT_CASSANDRA_ERRORS:
      retries_left = retries - 1
      if retries_left < 0:
        raise

      logger.debug('Unable to read batch status. Retrying.')
      is_applied = yield self.is_applied(retries=retries_left)
      raise gen.Return(is_applied)
    except IndexError:
      raise BatchNotFound(
        'Batch for {}:{} not found'.format(self.project, self.txid))
Пример #16
0
    def set_applied(self, retries=5):
        """ Mark the batch as being applied.

    Args:
      retries: The number of times to retry after failures.
    Raises:
      FailedBatch if the batch cannot be marked as applied.
    """
        if retries < 0:
            raise FailedBatch('Retries exhausted while updating batch')

        update_status = SimpleStatement("""
      UPDATE batch_status
      SET applied = True
      WHERE txid_hash = %(txid_hash)s
      IF op_id = %(op_id)s
    """,
                                        retry_policy=NO_RETRIES)
        parameters = {
            'txid_hash': tx_partition(self.project, self.txid),
            'op_id': self.op_id
        }

        try:
            result = yield self.tornado_cassandra.execute(
                update_status, parameters)
            if result.was_applied:
                self.applied = True
                return
        except TRANSIENT_CASSANDRA_ERRORS:
            pass  # Application is confirmed below.

        try:
            self.applied = yield self.is_applied()
            if self.applied:
                return
            yield self.set_applied(retries=retries - 1)
            return
        except (BatchNotFound, BatchNotOwned,
                TRANSIENT_CASSANDRA_ERRORS) as error:
            raise FailedBatch(str(error))
Пример #17
0
    def start(self, retries=5):
        """ Mark the batch as being in progress.

    Args:
      retries: The number of times to retry after failures.
    Raises:
      FailedBatch if the batch cannot be marked as being started.
    """
        if retries < 0:
            raise FailedBatch('Retries exhausted while starting batch')

        insert = SimpleStatement("""
      INSERT INTO batch_status (txid_hash, applied, op_id)
      VALUES (%(txid_hash)s, False, %(op_id)s)
      IF NOT EXISTS
    """,
                                 retry_policy=NO_RETRIES)
        parameters = {
            'txid_hash': tx_partition(self.project, self.txid),
            'op_id': self.op_id
        }

        try:
            result = yield self.tornado_cassandra.execute(insert, parameters)
        except TRANSIENT_CASSANDRA_ERRORS:
            yield self.start(retries=retries - 1)
            return

        if result.was_applied:
            return

        # Make sure this process was responsible for the insert.
        try:
            yield self.is_applied()
        except (BatchNotOwned, TRANSIENT_CASSANDRA_ERRORS) as batch_failure:
            raise FailedBatch(str(batch_failure))
        except BatchNotFound:
            yield self.start(retries=retries - 1)
            return
Пример #18
0
  def add_transactional_tasks(self, app, txid, tasks, service_id, version_id):
    """ Add tasks to be enqueued upon the completion of a transaction.

    Args:
      app: A string specifying an application ID.
      txid: An integer specifying a transaction ID.
      tasks: A list of TaskQueueAddRequest objects.
      service_id: A string specifying the client's service ID.
      version_id: A string specifying the client's version ID.
    """
    batch = BatchStatement(consistency_level=ConsistencyLevel.QUORUM,
                           retry_policy=BASIC_RETRIES)
    query_str = (
      'INSERT INTO transactions (txid_hash, operation, namespace, path, task) '
      'VALUES (?, ?, ?, ?, ?) '
      'USING TTL {ttl}'
    ).format(ttl=dbconstants.MAX_TX_DURATION * 2)
    insert = self.session.prepare(query_str)

    for task in tasks:
      task.clear_transaction()

      # The path for the task entry doesn't matter as long as it's unique.
      path = bytearray(str(uuid.uuid4()))

      task_payload = '_'.join([service_id, version_id, task.Encode()])
      args = (tx_partition(app, txid),
              TxnActions.ENQUEUE_TASK,
              '',
              path,
              task_payload)
      batch.add(insert, args)

    try:
      yield self.tornado_cassandra.execute(batch)
    except dbconstants.TRANSIENT_CASSANDRA_ERRORS:
      message = 'Exception while adding tasks in a transaction'
      logger.exception(message)
      raise AppScaleDBConnectionError(message)
Пример #19
0
  def add_transactional_tasks(self, app, txid, tasks, service_id, version_id):
    """ Add tasks to be enqueued upon the completion of a transaction.

    Args:
      app: A string specifying an application ID.
      txid: An integer specifying a transaction ID.
      tasks: A list of TaskQueueAddRequest objects.
      service_id: A string specifying the client's service ID.
      version_id: A string specifying the client's version ID.
    """
    batch = BatchStatement(consistency_level=ConsistencyLevel.QUORUM,
                           retry_policy=BASIC_RETRIES)
    query_str = (
      'INSERT INTO transactions (txid_hash, operation, namespace, path, task) '
      'VALUES (?, ?, ?, ?, ?) '
      'USING TTL {ttl}'
    ).format(ttl=dbconstants.MAX_TX_DURATION * 2)
    insert = self.session.prepare(query_str)

    for task in tasks:
      task.clear_transaction()

      # The path for the task entry doesn't matter as long as it's unique.
      path = bytearray(str(uuid.uuid4()))

      task_payload = '_'.join([service_id, version_id, task.Encode()])
      args = (tx_partition(app, txid),
              TxnActions.ENQUEUE_TASK,
              '',
              path,
              task_payload)
      batch.add(insert, args)

    try:
      yield self.tornado_cassandra.execute(batch)
    except dbconstants.TRANSIENT_CASSANDRA_ERRORS:
      message = 'Exception while adding tasks in a transaction'
      logging.exception(message)
      raise AppScaleDBConnectionError(message)
Пример #20
0
    def transactional_tasks_count(self, app, txid):
        """ Count the number of existing tasks associated with the transaction.

    Args:
      app: A string specifying an application ID.
      txid: An integer specifying a transaction ID.
    Returns:
      An integer specifying the number of existing tasks.
    """
        select = ('SELECT count(*) FROM transactions '
                  'WHERE txid_hash = %(txid_hash)s '
                  'AND operation = %(operation)s')
        parameters = {
            'txid_hash': tx_partition(app, txid),
            'operation': TxnActions.ENQUEUE_TASK
        }
        try:
            result = yield self.tornado_cassandra.execute(select, parameters)
            raise gen.Return(result[0].count)
        except dbconstants.TRANSIENT_CASSANDRA_ERRORS:
            message = 'Exception while fetching task count'
            logger.exception(message)
            raise AppScaleDBConnectionError(message)
Пример #21
0
  def transactional_tasks_count(self, app, txid):
    """ Count the number of existing tasks associated with the transaction.

    Args:
      app: A string specifying an application ID.
      txid: An integer specifying a transaction ID.
    Returns:
      An integer specifying the number of existing tasks.
    """
    select = (
      'SELECT count(*) FROM transactions '
      'WHERE txid_hash = %(txid_hash)s '
      'AND operation = %(operation)s'
    )
    parameters = {'txid_hash': tx_partition(app, txid),
                  'operation': TxnActions.ENQUEUE_TASK}
    try:
      result = yield self.tornado_cassandra.execute(select, parameters)
      raise gen.Return(result[0].count)
    except dbconstants.TRANSIENT_CASSANDRA_ERRORS:
      message = 'Exception while fetching task count'
      logger.exception(message)
      raise AppScaleDBConnectionError(message)
Пример #22
0
  def resolve(self, txid, composite_indexes):
    """ Resolves a large batch for a given transaction.

    Args:
      txid: An integer specifying a transaction ID.
      composite_indexes: A list of CompositeIndex objects.
    """
    txid_hash = tx_partition(self.project_id, txid)
    new_op_id = uuid.uuid4()
    try:
      batch_status = yield self._get_status(txid_hash)
    except BatchNotFound:
      # Make sure another process doesn't try to commit the transaction.
      yield self._insert(txid_hash, new_op_id)
      return

    old_op_id = batch_status.op_id
    yield self._update_op_id(txid_hash, batch_status.applied, old_op_id,
                             new_op_id)

    if batch_status.applied:
      # Make sure all the mutations in the batch have been applied.
      yield self._apply_mutations(txid, composite_indexes)
Пример #23
0
    def resolve(self, txid, composite_indexes):
        """ Resolves a large batch for a given transaction.

    Args:
      txid: An integer specifying a transaction ID.
      composite_indexes: A list of CompositeIndex objects.
    """
        txid_hash = tx_partition(self.project_id, txid)
        new_op_id = uuid.uuid4()
        try:
            batch_status = yield self._get_status(txid_hash)
        except BatchNotFound:
            # Make sure another process doesn't try to commit the transaction.
            yield self._insert(txid_hash, new_op_id)
            return

        old_op_id = batch_status.op_id
        yield self._update_op_id(txid_hash, batch_status.applied, old_op_id,
                                 new_op_id)

        if batch_status.applied:
            # Make sure all the mutations in the batch have been applied.
            yield self._apply_mutations(txid, composite_indexes)
Пример #24
0
    def start_transaction(self, app, txid, is_xg, in_progress):
        """ Persist transaction metadata.

    Args:
      app: A string containing an application ID.
      txid: An integer specifying the transaction ID.
      is_xg: A boolean specifying that the transaction is cross-group.
      in_progress: An iterable containing transaction IDs.
    """
        if in_progress:
            in_progress_bin = bytearray(
                struct.pack('q' * len(in_progress), *in_progress))
        else:
            in_progress_bin = None

        insert = (
            'INSERT INTO transactions (txid_hash, operation, namespace, path,'
            '                          start_time, is_xg, in_progress)'
            'VALUES (%(txid_hash)s, %(operation)s, %(namespace)s, %(path)s,'
            '        %(start_time)s, %(is_xg)s, %(in_progress)s)'
            'USING TTL {ttl}').format(ttl=dbconstants.MAX_TX_DURATION * 2)
        parameters = {
            'txid_hash': tx_partition(app, txid),
            'operation': TxnActions.START,
            'namespace': '',
            'path': bytearray(''),
            'start_time': datetime.datetime.utcnow(),
            'is_xg': is_xg,
            'in_progress': in_progress_bin
        }

        try:
            yield self.tornado_cassandra.execute(insert, parameters)
        except dbconstants.TRANSIENT_CASSANDRA_ERRORS:
            message = 'Exception while starting a transaction'
            logger.exception(message)
            raise AppScaleDBConnectionError(message)
Пример #25
0
  def start_transaction(self, app, txid, is_xg, in_progress):
    """ Persist transaction metadata.

    Args:
      app: A string containing an application ID.
      txid: An integer specifying the transaction ID.
      is_xg: A boolean specifying that the transaction is cross-group.
      in_progress: An iterable containing transaction IDs.
    """
    if in_progress:
      in_progress_bin = bytearray(
        struct.pack('q' * len(in_progress), *in_progress))
    else:
      in_progress_bin = None

    insert = (
      'INSERT INTO transactions (txid_hash, operation, namespace, path,'
      '                          start_time, is_xg, in_progress)'
      'VALUES (%(txid_hash)s, %(operation)s, %(namespace)s, %(path)s,'
      '        %(start_time)s, %(is_xg)s, %(in_progress)s)'
      'USING TTL {ttl}'
    ).format(ttl=dbconstants.MAX_TX_DURATION * 2)
    parameters = {'txid_hash': tx_partition(app, txid),
                  'operation': TxnActions.START,
                  'namespace': '',
                  'path': bytearray(''),
                  'start_time': datetime.datetime.utcnow(),
                  'is_xg': is_xg,
                  'in_progress': in_progress_bin}

    try:
      yield self.tornado_cassandra.execute(insert, parameters)
    except dbconstants.TRANSIENT_CASSANDRA_ERRORS:
      message = 'Exception while starting a transaction'
      logger.exception(message)
      raise AppScaleDBConnectionError(message)
Пример #26
0
    def claim(self):
        """ Claim a batch so that other processes don't work on it.

    Raises:
      FailedBatch if the batch cannot be claimed.
    """
        try:
            if self.is_applied():
                self.applied = True
        except TRANSIENT_CASSANDRA_ERRORS as error:
            raise FailedBatch(str(error))
        except BatchNotOwned:
            # This process does not own the batch yet.
            pass
        except BatchNotFound:
            # Make sure another process doesn't try to start.
            return self.start()

        update_id = SimpleStatement("""
      UPDATE batch_status
      SET op_id = %(new_op_id)s
      WHERE txid_hash = %(txid_hash)s
      IF op_id = %(old_op_id)s
    """,
                                    retry_policy=NO_RETRIES)
        parameters = {
            'txid_hash': tx_partition(self.project, self.txid),
            'new_op_id': self.op_id,
            'old_op_id': self.read_op_id
        }

        try:
            result = self.session.execute(update_id, parameters)
            assert result.was_applied
        except (TRANSIENT_CASSANDRA_ERRORS, AssertionError):
            raise FailedBatch('Unable to claim batch')