Beispiel #1
0
        /// <summary>
        /// Attempt to obtain a lock
        /// </summary>
        /// <param name="version">the version which was known</param>
        /// <param name="sessionKey"></param>
        /// <returns>true if successful</returns>
        public static bool Lock(DbConnection db, DatonKey key, string version, string sessionKey)
        {
            //assuming first there is a record, attempt to get the lock by updating it
            using (var cmd = db.CreateCommand())
            {
                cmd.CommandText = "update RetroLock set Touched=@t, LockedBy=@s where DatonKey=@k and DatonVersion=@v and (LockedBy is null or Touched<@old)";
                Utils.AddParameterWithValue(cmd, "t", DateTime.UtcNow);
                Utils.AddParameterWithValue(cmd, "s", sessionKey);
                Utils.AddParameterWithValue(cmd, "k", key.ToString());
                Utils.AddParameterWithValue(cmd, "v", version);
                Utils.AddParameterWithValue(cmd, "old", DateTime.UtcNow.AddSeconds(-120));
                int nrows = cmd.ExecuteNonQuery();
                if (nrows == 1)
                {
                    return(true);
                }
            }

            //update touched date: this tells us if the record exists, and ensures it won't be cleaned up during the lock process;
            //also unlock it if the lock is too old
            //bool recordExists;
            //using (var cmd = db.CreateCommand())
            //{
            //    cmd.CommandText = "update RetroLock set LockedBy=(case when Touched<@old then null else LockedBy end), Touched=@t where DatonKey=@k";
            //    Utils.AddParameterWithValue(cmd, "old", DateTime.UtcNow.AddSeconds(-120));
            //    Utils.AddParameterWithValue(cmd, "t", DateTime.UtcNow);
            //    Utils.AddParameterWithValue(cmd, "k", key.ToString());
            //    int nrows = cmd.ExecuteNonQuery();
            //    recordExists = nrows == 1;
            //}

            //reached here, so there was no record; create one with lock
            //(this won't occur if everything is working, since reading the version should happen before attempting to lock)
            using (var cmd = db.CreateCommand())
            {
                cmd.CommandText = "insert into RetroLock (DatonKey,DatonVersion,Touched,LockedBy) values(@k,@v,@t,@s)";
                Utils.AddParameterWithValue(cmd, "k", key.ToString());
                Utils.AddParameterWithValue(cmd, "v", version);
                Utils.AddParameterWithValue(cmd, "t", DateTime.UtcNow);
                Utils.AddParameterWithValue(cmd, "s", sessionKey);
                try
                {
                    cmd.ExecuteNonQuery();
                    return(true);
                }
                catch
                {
                    return(false); //another user created the lock record since we queried it above
                }
            }
        }
Beispiel #2
0
        /// <summary>
        /// Get the daton version code and session key holding the lock if any; if the lock row was missing, create it and assign
        /// the version
        /// </summary>
        /// <returns>(version,lockedBy) where lockedBy might be null</returns>
        public static (string, string) GetVersion(DbConnection db, DatonKey key)
        {
retry:

            //read existing row
            using (var cmd = db.CreateCommand())
            {
                cmd.CommandText = "select DatonVersion,LockedBy from RetroLock where DatonKey=@k";
                var p = cmd.CreateParameter();
                p.ParameterName = "k";
                p.Value         = key.ToString();
                cmd.Parameters.Add(p);
                using (var rdr = cmd.ExecuteReader())
                {
                    if (rdr.Read())
                    {
                        return(Utils.Read <string>(rdr, 0), Utils.Read <string>(rdr, 1));
                    }
                }
            }

            //not found, so create it
            string version = Guid.NewGuid().ToString();

            using (var cmd = db.CreateCommand())
            {
                try
                {
                    cmd.CommandText = "insert into RetroLock (DatonKey,DatonVersion,Touched) values(@k,@v,@t)";
                    Utils.AddParameterWithValue(cmd, "k", key.ToString());
                    Utils.AddParameterWithValue(cmd, "v", version);
                    Utils.AddParameterWithValue(cmd, "t", DateTime.UtcNow);
                    cmd.ExecuteNonQuery();
                    return(version, null);
                }
                catch
                {
                    //rare failure: another user created the row between when we queried and attempted the insert
                    Thread.Sleep(1);
                    goto retry;
                }
            }
        }
Beispiel #3
0
 /// <summary>
 /// Update the touched column for the daton, only if locked by the given session.
 /// </summary>
 public static void Touch(DbConnection db, DatonKey key, string sessionKey)
 {
     using (var cmd = db.CreateCommand())
     {
         cmd.CommandText = "update RetroLock set Touched=@t where DatonKey=@k and LockedBy=@s";
         Utils.AddParameterWithValue(cmd, "t", DateTime.UtcNow);
         Utils.AddParameterWithValue(cmd, "k", key.ToString());
         Utils.AddParameterWithValue(cmd, "s", sessionKey);
         cmd.ExecuteNonQuery();
     }
 }
Beispiel #4
0
        /// <summary>
        /// Unlock a daton and optionally assign new version; only has an effect if it was locked by the given session
        /// </summary>
        /// <param name="datonWasWritten">pass true if this unlock is following a write, or false if it is an abandoned lock</param>
        /// <returns>success flag and the new version (version only returned if datonWasWritten)</returns>
        public static (bool, string) Unlock(DbConnection db, DatonKey key, string sessionKey, bool datonWasWritten, int serverLifeNumber)
        {
            string version = datonWasWritten ? Guid.NewGuid().ToString() : null;

            using (var cmd = db.CreateCommand())
            {
                string versionsql = datonWasWritten ? ",DatonVersion=@v,UpdatedByServer=@u" : "";
                cmd.CommandText = $"update RetroLock set LockedBy=null,Touched=@t{versionsql} where DatonKey=@k and LockedBy=@s";
                Utils.AddParameterWithValue(cmd, "t", DateTime.UtcNow);
                Utils.AddParameterWithValue(cmd, "k", key.ToString());
                Utils.AddParameterWithValue(cmd, "s", sessionKey);
                if (datonWasWritten)
                {
                    Utils.AddParameterWithValue(cmd, "v", version);
                    Utils.AddParameterWithValue(cmd, "u", serverLifeNumber);
                }
                int  nrows   = cmd.ExecuteNonQuery();
                bool success = nrows == 1;
                return(success, version);
            }
        }