Ejemplo n.º 1
0
    def _write_mt(self, mt, txid):
        """Write out the minitransaction intent to RAMCloud.

        Uses the table identified by C{txramcloud.tx_table} and the transaction
        id stored in C{mt}.

        @param mt: the minitransaction
        @type  mt: L{MiniTransaction}

        @param txid: the object ID at which to write the minitransaction
        @type  txid: C{int}

        @return: the version of the object written
        @rtype:  L{int}

        @raise TransactionExpired: A tombstone is in the way.
        """

        rr = ramcloud.RejectRules(object_exists=True)
        try:
            return RAMCloud.write_rr(self, self.tx_table, txid, serialize(mt),
                                     rr)
        except ramcloud.ObjectExistsError:
            # looks like a tombstone is here
            raise self.TransactionExpired()
Ejemplo n.º 2
0
    def _apply_op(self, table_id, key, txid, op):
        """Apply the operation to its masked object.

        @param table_id: The table containing the possibly masked object.
        @type  table_id: C{int}

        @param key: The object ID of the possibly masked object.
        @type  key: C{int}

        @param op: the operation to apply
        @type  op: L{MTOperation}
        """

        read_rr = ramcloud.RejectRules(object_doesnt_exist=True)
        try:
            blob, version = RAMCloud.read_rr(self, table_id, key, read_rr)
        except ramcloud.NoObjectError:
            # does not exist, so not masked by txid
            return
        otxid, otimeout, odata = unpack(blob)
        if otxid == 0 or otxid != txid:
            # not masked by txid
            return

        # So, txid is indeed masking the object we read.

        if type(op) == MTOperation:
            # no op
            # remove mask / delete seed
            data = odata
        elif type(op) == MTWrite:
            data = op.data
        elif type(op) == MTDelete:
            data = None
        else:
            raise TxRAMCloud.InconsistencyError("Unknown type in MT: %s" %
                                                type(op))

        # Now, (data is None) determines whether we need to delete or update the
        # object at version.

        update_rr = ramcloud.RejectRules.exactly(version)
        if data is None:
            # delete the object at version
            try:
                RAMCloud.delete_rr(self, table_id, key, update_rr)
            except (ramcloud.NoObjectError, ramcloud.VersionError):
                # we must be racing another client to clean this up
                return
        else:
            # update the object at version with data
            blob = pack(0, 0, data)
            try:
                RAMCloud.write_rr(self, table_id, key, blob, update_rr)
            except (ramcloud.NoObjectError, ramcloud.VersionError):
                # we must be racing another client to clean this up
                return
Ejemplo n.º 3
0
    def _unmask_object(self, table_id, key, txid):
        """Ensure an object is not masked by a particular transaction.

        This operation is idempotent.

        @param table_id: The table containing the possibly masked object.
        @type  table_id: C{int}

        @param key: The object ID of the possibly masked object.
        @type  key: C{int}

        @param txid: The transaction which may be masking the object.
        @type  txid: C{int}

        """
        for retry in RetryStrategy():

            rr_read = ramcloud.RejectRules(object_doesnt_exist=True)
            try:
                blob, version = RAMCloud.read_rr(self, table_id, key, rr_read)
            except ramcloud.NoObjectError:
                # the object doesn't exist, so it's most certainly not masked
                return

            otxid, otimeout, data = unpack(blob)
            if otxid == 0 or otxid != txid:
                # the object is already not masked by this transaction
                return

            rr_change = ramcloud.RejectRules(version_gt_given=True,
                                             given_version=version)
            if data is None:
                # seed object
                try:
                    RAMCloud.delete_rr(self, table_id, key, rr_change)
                except ramcloud.VersionError:
                    retry.later()
            else:
                # full object
                blob = pack(0, 0, data)
                try:
                    RAMCloud.write_rr(self, table_id, key, blob, rr_change)
                except ramcloud.VersionError:
                    retry.later()
Ejemplo n.º 4
0
    def _delete_tombstone(self, txid):
        """Ensure a tombstone has been deleted from RAMCloud.

        @warning: No one should ever commit a transaction under txid following
        this operation.

        @param txid: the transaction id of the tombstone to delete
        @type  txid: C{int}
        """

        rr = ramcloud.RejectRules()
        RAMCloud.delete_rr(self, self.tx_table, txid, rr)
Ejemplo n.º 5
0
    def _write_unsafe(self, table_id, key, blob, user_reject_rules):
        assert not user_reject_rules.object_doesnt_exist
        assert not user_reject_rules.object_exists
        assert not user_reject_rules.version_gt_given

        for retry in RetryStrategy():
            try:
                # self.read_rr() makes sure the object is not masked
                version = self.read_rr(table_id, key, user_reject_rules)[1]
            except ramcloud.NoObjectError:
                # If there was no object, we demand that there be no object.
                reject_rules = ramcloud.RejectRules(object_exists=True)
            else:
                # If there was an object, we demand that there be either no
                # object or this particular version.
                reject_rules = ramcloud.RejectRules(version_gt_given=True,
                                                    given_version=version)
            try:
                return RAMCloud.write_rr(self, table_id, key, blob,
                                         reject_rules)
            except (ramcloud.ObjectExistsError, ramcloud.VersionError):
                retry.later()
Ejemplo n.º 6
0
    def _delete_unsafe(self, table_id, key, user_reject_rules):
        # - Throws L{ramcloud.NoObjectError} if user_reject_rules specifies
        # object_doesnt_exist and there is no object to be deleted.
        # - Doesn't throw L{ramcloud.ObjectExists}.
        # - Throws L{ramcloud.VersionError} if user_reject_rules specifies
        # version_eq_given and the version read matches the version given
        # or is older than the version given.

        assert not user_reject_rules.object_exists
        assert not user_reject_rules.version_gt_given

        for retry in RetryStrategy():
            try:
                # self.read_rr() makes sure there is object is not masked
                version = self.read_rr(table_id, key, user_reject_rules)[1]
            except (ramcloud.VersionError):
                raise
            except ramcloud.NoObjectError:
                # If there was no object, we're done.
                if user_reject_rules.object_doesnt_exist:
                    raise
                else:
                    return

            # We can stop worrying about the user's given version now:
            # If user_reject_rules.version_eq_given, there is a given version
            # that we must reject. But if we've made it this far, the version
            # we read is greater than user_reject_rules.given_version.

            # However, we still have to worry about NoObjectError iff
            # user_reject_rules.doesnt_exist.

            # There was an object when we called read. We're definitely good if
            # we delete the exact version we read. If there is no object to
            # delete now, we want our delete rejected iff user_reject_rules has
            # object_doesnt_exist.
            reject_rules = ramcloud.RejectRules(
                object_doesnt_exist=user_reject_rules.object_doesnt_exist,
                version_gt_given=True,
                given_version=version)
            try:
                return RAMCloud.delete_rr(self, table_id, key, reject_rules)
            except ramcloud.NoObjectError:
                if user_reject_rules.object_doesnt_exist:
                    raise
                else:
                    return
            except ramcloud.VersionError:
                retry.later()
Ejemplo n.º 7
0
    def _delete_mt(self, txid, version):
        """Ensure a minitransaction intent has been deleted from RAMCloud.

        @precondition: No references exist to C{txid} in the RAMCloud.

        @param txid: the transaction id of the minitransaction to delete
        @type  txid: C{int}

        @param version: the version number of the mt when it was read
        @type  version: C{int}
        """

        rr = ramcloud.RejectRules(version_gt_given=True, given_version=version)
        try:
            RAMCloud.delete_rr(self, self.tx_table, txid, rr)
        except ramcloud.VersionError:
            raise self.InconsistencyError("txid has been modified")
Ejemplo n.º 8
0
    def _write_tombstone(self, txid):
        """Force a transaction to abort.

        Writes a tombstone at txid if no object exists there.

        @param txid: The ID of the transaction to abort.
        @type  txid: C{int}

        @return: The version number of the tombstone written, or C{None} if an
        object already exists at txid.
        @rtype: C{int} or C{None}
        """

        rr = ramcloud.RejectRules(object_exists=True)
        blob = serialize(Tombstone())
        try:
            return RAMCloud.write_rr(self, self.tx_table, txid, blob, rr)
        except ramcloud.ObjectExistsError:
            return None
Ejemplo n.º 9
0
    def _clean(self, table_id, key, txid, timeout):
        """Clean a mask off of an object.

        This method won't necessarily do anything productive every time it's
        called, so it should be called inside a retry loop.

        @param table_id: The table containing the masked object.
        @type  table_id: C{int}

        @param key: The object ID of the masked object.
        @type  key: C{int}

        @param txid: The transaction which is masking the object.
        @type  txid: C{int}

        @param timeout: The time at which the mask expires.
        @type  timeout: C{int}
        """

        rr = ramcloud.RejectRules(object_doesnt_exist=True)
        try:
            mtdata, mtversion = RAMCloud.read_rr(self, self.tx_table, txid, rr)
        except ramcloud.NoObjectError:
            # txid doesn't exist, so wait or write tombstone
            if time.time() > timeout:
                # try to write a tombstone
                if self._write_tombstone(txid) is not None:
                    self._unmask_object(table_id, key, txid)
            # caller will retry if the object still needs cleaning
        else:
            try:
                mt = unserialize(mtdata)
            except pickle.BadPickleGet:
                raise self.InconsistencyError("Not a pickled object")
            if type(mt) == Tombstone:
                self._unmask_object(table_id, key, txid)
            elif type(mt) == MiniTransaction:
                self._finish_mt(mt, txid, mtversion)
            else:
                raise self.InconsistencyError("Not a MiniTransaction or " +
                                              "Tombstone")
Ejemplo n.º 10
0
 def _read_rr(self, table_id, key, user_reject_rules):
     # We can't reject for object_exists in RAMCloud.read_rr because of seed
     # objects, so we handle it later.
     reject_rules = ramcloud.RejectRules(object_doesnt_exist=True,
                         version_eq_given=user_reject_rules.version_eq_given,
                         version_gt_given=user_reject_rules.version_gt_given,
                         given_version=user_reject_rules.given_version)
     start = time.time()
     for retry in RetryStrategy():
         blob, version = RAMCloud.read_rr(self, table_id, key, reject_rules)
         txid, timeout, data = unpack(blob)
         if txid:
             # there's plenty of room for optimizing round trips here
             timeout = min(start + 1, timeout)
             self._clean(table_id, key, txid, timeout)
             retry.later()
         else:
             # not masked
             if user_reject_rules.object_exists:
                 raise ramcloud.ObjectExistsError()
             else:
                 return data, version
Ejemplo n.º 11
0
    def _mask_object(self, table_id, key, txid, timeout, user_reject_rules):
        """Mask an object by a particular transaction.

        If the object is already masked, this method will wait for it to become
        unmasked.

        @warning: Unlike L{_unmask_object}, this operation is B{not} idempotent.

        @param table_id: The table containing the object to mask.
        @type  table_id: C{int}

        @param key: The object ID to mask.
        @type  key: C{int}

        @param txid: The transaction which will mask the object.
        @type  txid: C{int}

        @param timeout: The time at which to expire the mask.
        @type  timeout: C{int}

        @param user_reject_rules: The reasons for which to abort masking the
                                  object.
        @type  user_reject_rules: L{ramcloud.RejectRules}

        @raise Exception: If the object could not be masked because of
                          C{user_reject_rules}. Specifically, this is one of:
                          L{ramcloud.NoObjectError},
                          L{ramcloud.ObjectExistsError}, or
                          L{ramcloud.VersionError}.
                          Additionally, the exception will have the attributes
                          C{table} and C{oid} set to parameter C{table_id} and
                          C{key}.

        @return: The new version of the newly masked object.
        @rtype: C{int}
        """

        for retry in RetryStrategy():

            # self.read_rr() requires object_doesnt_exist to be set, but we can
            # still use the user's other reject rules.
            rr_read = ramcloud.RejectRules(
                object_doesnt_exist=True,
                object_exists=user_reject_rules.object_exists,
                version_eq_given=user_reject_rules.version_eq_given,
                version_gt_given=user_reject_rules.version_gt_given,
                given_version=user_reject_rules.given_version)
            try:
                data, version = self.read_rr(table_id, key, rr_read)
            except (ramcloud.ObjectExistsError, ramcloud.VersionError) as e:
                # The user asked for a reject
                e.table = table_id
                e.oid = key
                raise
            except ramcloud.NoObjectError as e:
                if user_reject_rules.object_doesnt_exist:
                    # The user asked for a reject
                    e.table = table_id
                    e.oid = key
                    raise
                else:
                    # The user didn't ask for a reject, but the object doesn't
                    # exist. We need to create a seed and must fail if an
                    # object is already present.
                    rr_write = ramcloud.RejectRules(object_exists=True)
                    blob = pack(txid, timeout, data=None)
            else:
                # There was an object when we called read. It has no mask, and
                # it isn't a seed. We need to write over the exact version we
                # read.
                rr_write = ramcloud.RejectRules.exactly(version)
                blob = pack(txid, timeout, data)

            # Now, blob and rr_write are set and we can attempt a write.
            try:
                return RAMCloud.write_rr(self, table_id, key, blob, rr_write)
            except (ramcloud.ObjectExistsError, ramcloud.NoObjectError,
                    ramcloud.VersionError):
                retry.later()