Ejemplo n.º 1
0
        /// <summary>
        /// Attempt to get a lock for updating a persiston. Fails if another user has it locked already.
        /// Succeeds efficiently if this user already had it locked.
        /// </summary>
        /// <returns>success flag and error reason code</returns>
        public (bool, string) RequestLock(DatonKey datonKey, string version, string sessionKey)
        {
            //check if already locked by this server
            if (Locks.TryGetValue(datonKey, out LockInfo linfo))
            {
                bool isLockedByMe = linfo.SessionKey == sessionKey;
                if (!isLockedByMe)
                {
                    return(false, Constants.ERRCODE_LOCK); //someone else on this server has it locked
                }
                return(true, null);                        //this session already has it locked
            }

            //attempt lock on database
            using (var lockdb = GetLockConnection())
            {
                if (RetroLock.Lock(lockdb, datonKey, version, sessionKey))
                {
                    //success
                    Locks[datonKey] = new LockInfo {
                        SessionKey = sessionKey, DatonKey = datonKey, OldVersion = version
                    };
                    return(true, null);
                }

                //failed, so determine why
                (string verifiedVersion, _) = RetroLock.GetVersion(lockdb, datonKey);
                if (verifiedVersion != version)
                {
                    return(false, Constants.ERRCODE_VERSION); //the most recent version is newer than the version known by the caller
                }
                return(false, Constants.ERRCODE_LOCK);        //someone else on another server has it locked
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Attempt to release a lock
        /// </summary>
        /// <returns>success flag and new version number (new version only provided if persiston was actually written)</returns>
        public (bool, string) ReleaseLock(DatonKey datonKey, string sessionKey)
        {
            //check if it is possible to unlock
            if (Locks.TryGetValue(datonKey, out LockInfo linfo))
            {
                bool isLockedByMe = linfo.SessionKey == sessionKey;
                if (!isLockedByMe)
                {
                    return(false, null);                 //can't unlock because someone else on this server has it locked
                }
            }
            else
            {
                return(false, null);   //can't unlock because it is not locked by anyone on this server
            }
            //attempt unlock on database
            using (var lockdb = GetLockConnection())
            {
                (bool unlockOK, string newVersion) = RetroLock.Unlock(lockdb, datonKey, sessionKey, linfo.WasWritten, ServerLifeNumber);
                Locks.TryRemove(datonKey, out _); //we forget in memory even if there was a database problem (should not happen)

                //propogate change (this is hooked up to code that pushes the change to subscribed sessions)
                if (linfo.WasWritten)
                {
                    ChangePropogator?.Invoke(datonKey, newVersion);
                }

                return(unlockOK, newVersion);
            }
        }
Ejemplo n.º 3
0
 /// <summary>
 /// Get the version of the persiston. This ALWAYS goes to the database so don't call this too much.
 /// The result is guaranteed and will assign a version if there was none recorded.
 /// </summary>
 public string GetVersion(DatonKey datonKey)
 {
     using (var lockdb = GetLockConnection())
     {
         (string version, _) = RetroLock.GetVersion(lockdb, datonKey);
         return(version);
     }
 }
Ejemplo n.º 4
0
        /// <summary>
        /// Update the database touched time for all current locks, so that other servers know that this server has not died.
        /// Then also obtain changes from other servers since last checked and return the new key and versions.
        /// Occasionally this also cleans out the lock table of old entries.
        /// </summary>
        public List <(DatonKey, string)> InterServerProcess()
        {
            var sinceUtc = LastCheckedOtherServers.AddSeconds(-5); //a little overlap

            LastCheckedOtherServers = DateTime.UtcNow;
            using (var lockdb = GetLockConnection())
            {
                //touch records that are still locked
                foreach (var linfo in Locks.Values)
                {
                    RetroLock.Touch(lockdb, linfo.DatonKey, linfo.SessionKey);
                }

                //cleanup every 12 hours
                if (DateTime.UtcNow > NextCleanup)
                {
                    NextCleanup = DateTime.UtcNow.AddHours(12);
                    RetroLock.Cleanup(lockdb);
                }

                //read from other servers
                return(RetroLock.GetRecentUpdatesByOtherServers(lockdb, sinceUtc, ServerLifeNumber));
            }
        }