Permalink
Browse files

Touch&Gat implementation.

  • Loading branch information...
1 parent 14ee46f commit 9c574e67929fc7f89bbd18642be2cb4df50db020 @enyim committed Mar 28, 2011
View
@@ -3,4 +3,5 @@
TestResults/*
*.suo
*.user
-build/output/*
+build/output/*
+_ReSharper*
@@ -3,7 +3,7 @@
namespace Enyim.Caching.Memcached.Protocol.Binary
{
- internal static class BinaryConverter
+ public static class BinaryConverter
{
public static unsafe int DecodeInt16(byte* buffer, int offset)
{
@@ -26,6 +26,7 @@ public static unsafe int DecodeInt32(byte* buffer, int offset)
return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
}
+
public static unsafe ulong DecodeUInt64(byte[] buffer, int offset)
{
fixed (byte* ptr = buffer)
@@ -10,14 +10,17 @@ public class BinaryRequest
private static readonly Enyim.Caching.ILog log = Enyim.Caching.LogManager.GetLogger(typeof(BinaryRequest));
private static int InstanceCounter;
- public OpCode Operation;
+ public byte Operation;
+ public readonly int CorrelationId;
+
public string Key;
public ulong Cas;
- public readonly int CorrelationId;
- public BinaryRequest(OpCode operation)
+ public BinaryRequest(OpCode operation) : this((byte)operation) { }
+
+ public BinaryRequest(byte commandCode)
{
- this.Operation = operation;
+ this.Operation = commandCode;
// session id
this.CorrelationId = Interlocked.Increment(ref InstanceCounter);
}
@@ -53,7 +56,7 @@ public unsafe IList<ArraySegment<byte>> CreateBuffer(IList<ArraySegment<byte>> a
fixed (byte* buffer = header)
{
buffer[0x00] = 0x80; // magic
- buffer[0x01] = (byte)this.Operation;
+ buffer[0x01] = this.Operation;
// key length
buffer[0x02] = (byte)(keyLength >> 8);
@@ -833,10 +833,10 @@ private static void SafeWaitAllAndDispose(WaitHandle[] waitHandles)
#region [ Expiration helper ]
- private const int MaxSeconds = 60 * 60 * 24 * 30;
- private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1);
+ protected const int MaxSeconds = 60 * 60 * 24 * 30;
+ protected static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1);
- private static uint GetExpiration(TimeSpan? validFor, DateTime? expiresAt)
+ protected static uint GetExpiration(TimeSpan? validFor, DateTime? expiresAt)
{
if (validFor != null && expiresAt != null)
throw new ArgumentException("You cannot specify both validFor and expiresAt.");
@@ -0,0 +1,39 @@
+using System;
+
+namespace Membase
+{
+ internal class BasicMembaseOperationFactory : Enyim.Caching.Memcached.Protocol.Binary.BinaryOperationFactory, IMembaseOperationFactory
+ {
+ internal static readonly BasicMembaseOperationFactory Instance = new BasicMembaseOperationFactory();
+
+ ITouchOperation IMembaseOperationFactory.Touch(string key, uint newExpiration)
+ {
+ return new TouchOperation(null, key, newExpiration);
+ }
+
+ IGetAndTouchOperation IMembaseOperationFactory.GetAndTouch(string key, uint newExpiration)
+ {
+ return new GetAndTouchOperation(null, key, newExpiration);
+ }
+ }
+}
+
+#region [ License information ]
+/* ************************************************************
+ *
+ * Copyright (c) 2010 Attila Kiskó, enyim.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ************************************************************/
+#endregion
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Enyim.Caching.Memcached;
+
+namespace Membase
+{
+ public interface IMembaseOperationFactory : IOperationFactory
+ {
+ ITouchOperation Touch(string key, uint newExpiration);
+ IGetAndTouchOperation GetAndTouch(string key, uint newExpiration);
+ }
+}
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Enyim.Caching.Memcached;
+
+namespace Membase
+{
+ public interface IMembaseServerPool : IServerPool
+ {
+ IMembaseOperationFactory OperationFactory { get; }
+ }
+}
View
@@ -54,11 +54,17 @@
<Compile Include="Configuration\UriElement.cs" />
<Compile Include="Configuration\UriElementCollection.cs" />
<Compile Include="Configuration\UriValidator.cs" />
+ <Compile Include="Operations\GetAndTouchOperation.cs" />
+ <Compile Include="BasicMembaseOperationFactory.cs" />
+ <Compile Include="OperationInterfaces.cs" />
<Compile Include="Deserialization.cs" />
+ <Compile Include="IMembaseOperationFactory.cs" />
+ <Compile Include="IMembaseServerPool.cs" />
<Compile Include="MessageStreamListener.cs" />
<Compile Include="MembaseClient.cs" />
<Compile Include="MembasePool.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Operations\TouchOperation.cs" />
<Compile Include="VBucketAwareOperationFactory.cs" />
<Compile Include="WebClientWithTimeout.cs">
<SubType>Component</SubType>
View
@@ -15,7 +15,7 @@ public class MembaseClient : MemcachedClient
private static readonly Enyim.Caching.ILog log = Enyim.Caching.LogManager.GetLogger(typeof(MembaseClient));
private static readonly IMembaseClientConfiguration DefaultConfig = (IMembaseClientConfiguration)ConfigurationManager.GetSection("membase");
- private MembasePool poolInstance;
+ private IMembaseServerPool poolInstance;
/// <summary>
/// Initializes a new instance of the <see cref="T:Membase.MembaseClient" /> class using the default configuration and bucket.
@@ -60,7 +60,7 @@ public class MembaseClient : MemcachedClient
configuration.CreateTranscoder(),
configuration.CreatePerformanceMonitor())
{
- this.poolInstance = (MembasePool)this.Pool;
+ this.poolInstance = (IMembaseServerPool)this.Pool;
}
/// <summary>Obsolete. Use .ctor(bucket, password) to explicitly set the bucket password.</summary>
@@ -228,6 +228,101 @@ private bool ExecuteWithRedirect(IMemcachedNode startNode, ISingleItemOperation
return false;
}
+
+ public void Touch(string key, DateTime nextExpiration)
+ {
+ PerformTouch(key, GetExpiration(null, nextExpiration));
+ }
+
+ public void Touch(string key, TimeSpan nextExpiration)
+ {
+ PerformTouch(key, GetExpiration(nextExpiration, null));
+ }
+
+ protected void PerformTouch(string key, uint nextExpiration)
+ {
+ var hashedKey = this.KeyTransformer.Transform(key);
+ var node = this.Pool.Locate(hashedKey);
+
+ if (node != null)
+ {
+ var command = this.poolInstance.OperationFactory.Touch(key, nextExpiration);
+ var retval = ExecuteWithRedirect(node, command);
+ }
+ }
+
+ public object Get(string key, DateTime newExpiration)
+ {
+ object tmp;
+
+ return this.TryGet(key, newExpiration, out tmp) ? tmp : null;
+ }
+
+ public T Get<T>(string key, DateTime newExpiration)
+ {
+ object tmp;
+
+ return TryGet(key, newExpiration, out tmp) ? (T)tmp : default(T);
+ }
+
+ public bool TryGet(string key, DateTime newExpiration, out object value)
+ {
+ ulong cas = 0;
+
+ return this.PerformTryGetAndTouch(key, MemcachedClient.GetExpiration(null, newExpiration), out cas, out value);
+ }
+
+ public CasResult<object> GetWithCas(string key, DateTime newExpiration)
+ {
+ return this.GetWithCas<object>(key, newExpiration);
+ }
+
+ public CasResult<T> GetWithCas<T>(string key, DateTime newExpiration)
+ {
+ CasResult<object> tmp;
+
+ return this.TryGetWithCas(key, newExpiration, out tmp)
+ ? new CasResult<T> { Cas = tmp.Cas, Result = (T)tmp.Result }
+ : new CasResult<T> { Cas = tmp.Cas, Result = default(T) };
+ }
+
+ public bool TryGetWithCas(string key, DateTime newExpiration, out CasResult<object> value)
+ {
+ object tmp;
+ ulong cas;
+
+ var retval = this.PerformTryGetAndTouch(key, MemcachedClient.GetExpiration(null, newExpiration), out cas, out tmp);
+
+ value = new CasResult<object> { Cas = cas, Result = tmp };
+
+ return retval;
+ }
+
+ protected bool PerformTryGetAndTouch(string key, uint nextExpiration, out ulong cas, out object value)
+ {
+ var hashedKey = this.KeyTransformer.Transform(key);
+ var node = this.Pool.Locate(hashedKey);
+
+ if (node != null)
+ {
+ var command = this.poolInstance.OperationFactory.GetAndTouch(hashedKey, nextExpiration);
+
+ if (this.ExecuteWithRedirect(node, command))
+ {
+ value = this.Transcoder.Deserialize(command.Result);
+ cas = command.CasValue;
+ if (this.PerformanceMonitor != null) this.PerformanceMonitor.Get(1, true);
+
+ return true;
+ }
+ }
+
+ value = null;
+ cas = 0;
+ if (this.PerformanceMonitor != null) this.PerformanceMonitor.Get(1, false);
+
+ return false;
+ }
}
}
View
@@ -13,7 +13,7 @@ namespace Membase
/// <summary>
/// Socket pool using the Membase server's dynamic node list
/// </summary>
- internal class MembasePool : IServerPool
+ internal class MembasePool : IMembaseServerPool
{
private static readonly Enyim.Caching.ILog log = Enyim.Caching.LogManager.GetLogger(typeof(MembasePool));
@@ -236,7 +236,7 @@ private InternalState InitBasic(ClusterConfig config, ISaslAuthenticationProvide
{
CurrentNodes = tmp.ToArray(),
Locator = this.configuration.CreateNodeLocator() ?? new KetamaNodeLocator(),
- OpFactory = new Enyim.Caching.Memcached.Protocol.Binary.BinaryOperationFactory()
+ OpFactory = BasicMembaseOperationFactory.Instance
};
}
@@ -421,6 +421,11 @@ IOperationFactory IServerPool.OperationFactory
get { return this.state.OpFactory; }
}
+ IMembaseOperationFactory IMembaseServerPool.OperationFactory
+ {
+ get { return this.state.OpFactory; }
+ }
+
IEnumerable<IMemcachedNode> IServerPool.GetWorkingNodes()
{
return this.state.Locator.GetWorkingNodes();
@@ -456,7 +461,7 @@ private class InternalState
public IMemcachedNodeLocator Locator;
public VBucketNodeLocator ForwardLocator;
- public IOperationFactory OpFactory;
+ public IMembaseOperationFactory OpFactory;
public IMemcachedNode[] CurrentNodes;
// if this is false, it's safe to reinitialize/recreate the locator when a server goes offline
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Enyim.Caching.Memcached;
+
+namespace Membase
+{
+ public interface ITouchOperation : ISingleItemOperation { }
+ public interface IGetAndTouchOperation : IGetOperation { }
+}
Oops, something went wrong.

3 comments on commit 9c574e6

@AndrewLane

Looking forward to trying this out...

@enyim
Owner
enyim replied Jun 8, 2011

You already can: http://www.couchbase.org/products/membase/1-7-series (it's a beta tho)

@enyim
Owner
enyim replied Jun 8, 2011

Actually it seems to be the final version.

Please sign in to comment.