def __init__(self, store, subject, lease_time=None, token=None):
    """Ensure we can take a lock on this subject."""
    super(SqliteTransaction, self).__init__(store,
                                            utils.SmartUnicode(subject),
                                            lease_time=lease_time,
                                            token=token)

    if lease_time is None:
      lease_time = config_lib.CONFIG["Datastore.transaction_timeout"]

    self.lock_token = thread.get_ident()
    sqlite_connection = store.cache.Get(self.subject)

    # We first check if there is a lock on the subject.
    # Next we set our lease time and lock_token as identification.
    with sqlite_connection:
      locked_until, stored_token = sqlite_connection.GetLock(subject)

      # This is currently locked by another thread.
      if locked_until and time.time() < float(locked_until):
        raise data_store.TransactionError("Subject %s is locked" % subject)

      # Subject is not locked, we take a lease on it.
      self.expires = time.time() + lease_time
      sqlite_connection.SetLock(subject, self.expires, self.lock_token)

    # Check if the lock stuck. If the stored token is not ours
    # then probably someone was able to grab it before us.
    locked_until, stored_token = sqlite_connection.GetLock(subject)
    if stored_token != self.lock_token:
      raise data_store.TransactionError("Unable to lock subject %s" % subject)

    self.locked = True
    def __init__(self, store, subject, lease_time=None, token=None):
        """Ensure we can take a lock on this subject."""
        self.store = store
        self.token = token
        self.subject = utils.SmartUnicode(subject)
        self.object_id = objectid.ObjectId(
            hashlib.sha256(utils.SmartStr(self.subject)).digest()[:12])

        self.to_set = {}
        self.to_delete = set()
        if lease_time is None:
            lease_time = config_lib.CONFIG["Datastore.transaction_timeout"]

        self.expires = time.time() + lease_time
        self.document = self.store.latest_collection.find_and_modify(
            query={
                "_id": self.object_id,
                "expires": {
                    "$lt": time.time()
                }
            },
            update=dict(_id=self.object_id, expires=self.expires),
            upsert=False,
            new=True)

        if self.document:
            # Old transaction expired and we hold a lock now:
            self.locked = True
            return

        # Maybe the lock did not exist yet. To create it, we use a lock to reduce
        # the chance of deleting some other lock created at the same time. Note that
        # there still exists a very small race if this happens in multiple processes
        # at the same time.
        with self.lock_creation_lock:
            document = self.store.latest_collection.find(
                {"_id": self.object_id})
            if not document.count():
                self.UpdateLease(lease_time)

                cursor = self.store.latest_collection.find(
                    {"_id": self.object_id})
                if cursor.count() != 1:
                    self._DeleteLock()
                    logging.warn("Multiple lock rows for %s", subject)
                    raise data_store.TransactionError(
                        "Error while locking %s." % subject)

                self.document = cursor.next()

                if self.document["expires"] != self.expires:
                    raise data_store.TransactionError("Subject %s is locked" %
                                                      subject)

                # We hold a lock now:
                self.locked = True
                return

        raise data_store.TransactionError("Subject %s is locked" % subject)
Exemple #3
0
    def _CheckForLock(self):
        """Checks that the lock has stuck."""

        query = ("SELECT lock_expiration, lock_owner FROM locks "
                 "WHERE subject_hash=unhex(md5(%s))")
        args = [self.subject]
        rows = self.store.ExecuteQuery(query, args)
        for row in rows:

            # We own this lock now.
            if (row["lock_expiration"] == self.expires_lock
                    and row["lock_owner"] == self.lock_token):
                return

            else:
                # Someone else owns this lock.
                raise data_store.TransactionError("Subject %s is locked" %
                                                  self.subject)

        # If we get here the row does not exist:
        query = ("INSERT IGNORE INTO locks "
                 "SET lock_expiration=%s, lock_owner=%s, "
                 "subject_hash=unhex(md5(%s))")
        args = [self.expires_lock, self.lock_token, self.subject]
        self.store.ExecuteQuery(query, args)

        self._CheckForLock()
Exemple #4
0
 def _DeleteLock(self):
     # Deletes the lock entirely from the document.
     document = dict(_id=self.object_id, expires=self.expires)
     if not self.store.latest_collection.remove(query=document):
         raise data_store.TransactionError("Could not remove lock for %s." %
                                           self.subject)
     self.locked = False
Exemple #5
0
    def __init__(self, store, subject, lease_time=None, token=None):
        """Ensure we can take a lock on this subject."""
        super(TDBTransaction, self).__init__(store,
                                             utils.SmartUnicode(subject),
                                             lease_time=lease_time,
                                             token=token)

        self.lock_key = utils.SmartUnicode(self.subject) + "_lock"
        if lease_time is None:
            lease_time = config_lib.CONFIG["Datastore.transaction_timeout"]

        # Note that we have the luxury of real file locking here so this will block
        # until we obtain the lock.
        with store.cache.Get(self.subject) as tdb_context:
            locked_until = tdb_context.Get(self.lock_key)

            # This is currently locked by another thread.
            if locked_until and time.time() < float(locked_until):
                raise data_store.TransactionError("Subject %s is locked" %
                                                  subject)

            # Subject is not locked, we take a lease on it.
            self.expires = time.time() + lease_time
            tdb_context.Put(self.expires, self.lock_key)
            self.locked = True
Exemple #6
0
    def __init__(self, ds, subject, token=None):
        ds.security_manager.CheckDataStoreAccess(token, [subject], "w")
        self.collection = ds.collection
        self.subject = subject
        self.token = token

        # Bring all the data over to minimize round trips
        encoded_cache = self.collection.find_one(EscapeKey(subject)) or {}
        self._cache = {}
        for (k, v) in encoded_cache.items():
            self._cache[DecodeKey(k)] = v

        # Initial lock number is a random number to avoid a race on creating new
        # documents.
        self.version = self._cache.get("_lock")
        if not self.version:
            # This object is not currently present or versioned, Create it
            try:
                self.collection.update(
                    dict(_id=EscapeKey(subject)),
                    {"$set": {
                        "_lock": random.randint(1, 1e6)
                    }},
                    upsert=True,
                    safe=True)
                # Re-read the lock to ensure we do not have a race
                self._cache = self.collection.find_one(EscapeKey(subject))
                self.version = self._cache.get("_lock")
            except errors.PyMongoError as e:
                logging.error(u"Mongo Error %s", utils.SmartUnicode(e))
                raise data_store.TransactionError(utils.SmartUnicode(e))

        self._to_update = {}
        self._committed = False
Exemple #7
0
 def _RemoveLock(self):
   # Remove the lock on the document.
   if not self.store.latest_collection.find_and_modify(
       query=self.document, update=dict(_id=self.object_id, expires=0)):
     raise data_store.TransactionError("Lock was overridden for %s." %
                                       self.subject)
   self.locked = False
Exemple #8
0
 def UpdateLease(self, duration):
   now = time.time()
   ret = self.store.ExtendSubjectLock(self.subject, self.transid, duration,
                                      self.token)
   if ret != self.transid:
     raise data_store.TransactionError("Unable to update the lease on %s" %
                                       self.subject)
   self.expires = now + duration
Exemple #9
0
  def __init__(self, store, subject, lease_time=None, token=None):
    """Ensure we can take a lock on this subject."""
    super(HTTPTransaction, self).__init__(
        store, utils.SmartUnicode(subject), lease_time=lease_time, token=token)

    if lease_time is None:
      lease_time = config_lib.CONFIG["Datastore.transaction_timeout"]

    now = time.time()
    self.transid = store.LockSubject(self.subject, lease_time, self.token)
    if not self.transid:
      raise data_store.TransactionError("Unable to lock subject %s" % subject)
    self.expires = now + lease_time
    self.locked = True
Exemple #10
0
  def __init__(self, store, subject, lease_time=None, token=None):
    super(FakeTransaction, self).__init__(store, subject, lease_time=lease_time,
                                          token=token)
    self.locked = False
    if lease_time is None:
      lease_time = config_lib.CONFIG["Datastore.transaction_timeout"]

    self.expires = time.time() + lease_time

    with self.store.lock:
      expires = store.transactions.get(subject)
      if expires and time.time() < expires:
        raise data_store.TransactionError("Subject is locked")

      # Check expiry time.
      store.transactions[subject] = self.expires

      self.locked = True
Exemple #11
0
    def __init__(self, store, subject, lease_time=None, token=None):
        self.data_store = store
        self.subject = subject
        self.token = token
        self.locked = False
        self.to_set = {}
        self.to_delete = set()
        if lease_time is None:
            lease_time = config_lib.CONFIG["Datastore.transaction_timeout"]

        self.expires = time.time() + lease_time

        with self.data_store.lock:
            expires = store.transactions.get(subject)
            if expires and time.time() < expires:
                raise data_store.TransactionError("Subject is locked")

            # Check expiry time.
            store.transactions[subject] = self.expires

            self.locked = True
Exemple #12
0
  def CheckForLock(self, connection, subject):
    """Checks that the lock has stuck."""

    for row in connection.Execute(
        "select * from `%s` where subject=%%s and hash=md5(%%s) and "
        "attribute='transaction'" % self.table_name, (subject, subject)):

      # We own this lock now.
      if row["value_integer"] == self.expires_lock:
        return

      # Someone else owns this lock.
      else:
        raise data_store.TransactionError("Subject %s is locked" % subject)

    # If we get here the row does not exist:
    connection.Execute(
        "insert ignore into `%s` set value_integer=%%s, "
        "attribute='transaction', subject=%%s, hash=md5(%%s) " %
        self.table_name, (self.expires_lock, self.subject, self.subject))

    self.CheckForLock(connection, subject)
Exemple #13
0
    def Commit(self):
        """Commit the transaction."""
        if self._committed:
            logging.error("Attempt to commit transaction multiple times...")
            return

        self._committed = True

        # We set the entire object now:
        if self._to_update:
            spec = dict(_id=URNEncode(EscapeKey(self.subject)))
            try:
                spec["_lock"] = self._cache["_lock"]
            except KeyError:
                pass

            self._to_update["_lock"] = self.version + 1
            # There are basically three cases here:

            # 1) The document does not already exist. Nothing will match spec, and
            #    this will insert a new document (because of the upsert=True). The
            #    transaction succeeds.

            # 2) The document exists and matches spec - the same document will be
            #    updated. The _lock version will be incremented. The transaction
            #    succeeds.

            # 3) The document exists but does not match spec. This is likely because
            #    someone else has modified it since we opened for read. We will try to
            #    add a new document (as in 1 above), but this will raise because there
            #    already is a document with the same ID. We therefore trap this
            #    exception and emit a TransactionError. Transaction fails.
            try:
                self.collection.update(spec, {"$set": self._to_update},
                                       upsert=True,
                                       safe=True)
            except errors.OperationFailure as e:
                # Transaction failed.
                raise data_store.TransactionError(utils.SmartUnicode(e))