internal override IEnumerable<RedisMessage> CreateMessages() { yield return RedisMessage.Create(db, RedisLiteral.WATCH, key); var msgResult = new MessageResultBoolean(this); // we want this ExecuteSynchronously so that we can exploit the ordered nature of the reader to check all the // results efficiently msgResult.Task.ContinueWith(testExisted, TaskContinuationOptions.ExecuteSynchronously); var message = hashField == null ? RedisMessage.Create(db, RedisLiteral.EXISTS, key) : RedisMessage.Create(db, RedisLiteral.HEXISTS, key, hashField); message.SetMessageResult(msgResult); yield return message; }
void IMultiMessage.Execute(RedisConnectionBase connection, ref int currentDb) { // note: composite command in a tight time-frame! most user-facing code won't look this bad; this is just ugly // because it is infrastructure code; tough! var existsResult = new MessageResultBoolean(); var existsMessage = RedisMessage.Create(Db, RedisLiteral.EXISTS, key); // watch the key; we want changes to cause abort existsMessage.SetMessageResult(existsResult); connection.WriteMessage(ref currentDb, existsMessage, null); connection.Flush(true); // make sure it goes to the server! if we wait for it, and it is stuck // in the buffer, we've deadlocked ourself // now, we need to issue the rest of the composite command immediately to avoid multiplex issues, // so we must wait on the EXISTS, and act accordingly bool exists = connection.Wait(existsResult.Task); if (exists) { // obviously locked; just unwatch and return false connection.WriteMessage(ref currentDb, RedisMessage.Create(Db, RedisLiteral.UNWATCH), null); base.Complete(RedisResult.Integer(0)); } else { // isn't obviously locked; try a multi/setnx/expire/exec; if someone else has touched the key, this will fail and // we'll return false; otherwise, we get a lock with an expiry set connection.WriteMessage(ref currentDb, RedisMessage.Create(-1, RedisLiteral.MULTI), null); var pending = new List<QueuedMessage>(); connection.WriteMessage(ref currentDb, RedisMessage.Create(Db, RedisLiteral.SETNX, key, value), pending); connection.WriteMessage(ref currentDb, RedisMessage.Create(Db, RedisLiteral.EXPIRE, key, timeout), pending); var execResult = new MessageLockResult(); var exec = RedisMessage.Create(-1, RedisLiteral.EXEC).Critical(); exec.SetMessageResult(execResult); execResult.Task.ContinueWith(task => { if (task.Status == TaskStatus.RanToCompletion) { base.Complete(RedisResult.Integer(task.Result ? 1 : 0)); } else { base.Complete(RedisResult.Error(GetErrorMessage(task.Exception))); } }); connection.WriteMessage(ref currentDb, exec, null); } }