Skip to content
This repository has been archived by the owner on Dec 24, 2022. It is now read-only.

Commit

Permalink
Add new overridable RedisManagerFactory to RedisSentinel with configu…
Browse files Browse the repository at this point in the history
…rable OnInit filter
  • Loading branch information
mythz committed Nov 18, 2014
1 parent 51efbdc commit 7912833
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 57 deletions.
52 changes: 52 additions & 0 deletions src/ServiceStack.Redis/RedisManagerFactory.cs
@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace ServiceStack.Redis
{
public class RedisManagerFactory
{
public Action<IRedisClientsManager> OnInit { get; set; }

public Func<IEnumerable<string>, IEnumerable<string>, IRedisClientsManager> FactoryFn { get; set; }

public RedisManagerFactory()
{
FactoryFn = CreatePooledRedisClientManager;
}

public static IRedisClientsManager CreatePooledRedisClientManager(
IEnumerable<string> readWriteHosts,
IEnumerable<string> readOnlyHosts)
{
return readOnlyHosts.Any()
? new PooledRedisClientManager(readWriteHosts, readOnlyHosts)
: new PooledRedisClientManager(readWriteHosts.ToArray());
}

public static IRedisClientsManager CreateBasicRedisClientManager(
IEnumerable<string> readWriteHosts,
IEnumerable<string> readOnlyHosts)
{
return readOnlyHosts.Any()
? new BasicRedisClientManager(readWriteHosts, readOnlyHosts)
: new BasicRedisClientManager(readWriteHosts.ToArray());
}

public static IRedisClientsManager CreateRedisManager(
IEnumerable<string> readWriteHosts,
IEnumerable<string> readOnlyHosts)
{
return new RedisManagerPool(readWriteHosts);
}

public IRedisClientsManager Create(IEnumerable<string> readWriteHosts, IEnumerable<string> readOnlyHosts)
{
var redisManager = FactoryFn(readWriteHosts, readOnlyHosts);
if (OnInit != null)
OnInit(redisManager);

return redisManager;
}
}
}
27 changes: 13 additions & 14 deletions src/ServiceStack.Redis/RedisSentinel.cs
Expand Up @@ -5,33 +5,32 @@
// Upon a s_down event, the RedisClientsManager will be failed over to the new set of slaves/masters
//

using ServiceStack;
using ServiceStack.Redis;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Web;

namespace ServiceStack.Redis
{
public class RedisSentinel : IRedisSentinel
{
public RedisManagerFactory RedisManagerFactory { get; set; }

private readonly string sentinelName;
private int failures = 0;
private int sentinelIndex = -1;
private List<string> sentinels;
private RedisSentinelWorker worker;
private PooledRedisClientManager clientManager;
internal IRedisClientsManager redisManager;
private static int MaxFailures = 5;

public RedisSentinel(IEnumerable<string> sentinelHosts, string sentinelName)
{
if (sentinelHosts == null || sentinelHosts.Count() == 0) throw new ArgumentException("sentinels must have at least one entry");
this.sentinels = sentinelHosts != null ? sentinelHosts.ToList() : null;
if (sentinelHosts == null || sentinels.Count == 0)
throw new ArgumentException("sentinels must have at least one entry");

this.sentinelName = sentinelName;
this.sentinels = new List<string>(sentinelHosts);
this.RedisManagerFactory = new RedisManagerFactory();
}

/// <summary>
Expand All @@ -42,22 +41,22 @@ public IRedisClientsManager Setup()
{
GetValidSentinel();

if (this.clientManager == null)
if (this.redisManager == null)
{
throw new ApplicationException("Unable to resolve sentinels!");
}

return this.clientManager;
return this.redisManager;
}

private void GetValidSentinel()
{
while (this.clientManager == null && ShouldRetry())
while (this.redisManager == null && ShouldRetry())
{
try
{
this.worker = GetNextSentinel();
this.clientManager = worker.GetClientManager();
this.redisManager = worker.GetClientManager();
this.worker.BeginListeningForConfigurationChanges();
}
catch (RedisException)
Expand All @@ -80,7 +79,7 @@ private void GetValidSentinel()
/// <remarks>This will be true if the failures is less than either RedisSentinel.MaxFailures or the # of sentinels, whatever is greater</remarks>
private bool ShouldRetry()
{
return this.failures < Math.Max(RedisSentinel.MaxFailures, this.sentinels.Count);
return this.failures < Math.Max(MaxFailures, this.sentinels.Count);
}

private RedisSentinelWorker GetNextSentinel()
Expand All @@ -92,7 +91,7 @@ private RedisSentinelWorker GetNextSentinel()
sentinelIndex = 0;
}

var sentinelWorker = new RedisSentinelWorker(sentinels[sentinelIndex], this.sentinelName, this.clientManager);
var sentinelWorker = new RedisSentinelWorker(this, sentinels[sentinelIndex], this.sentinelName);

sentinelWorker.SentinelError += Worker_SentinelError;
return sentinelWorker;
Expand Down
53 changes: 18 additions & 35 deletions src/ServiceStack.Redis/RedisSentinelWorker.cs
@@ -1,38 +1,35 @@
using ServiceStack;
using ServiceStack.Logging;
using ServiceStack.Redis;
using ServiceStack.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace ServiceStack.Redis
{
internal class RedisSentinelWorker : IDisposable
{
protected static readonly ILog Log = LogManager.GetLogger(typeof(RedisSentinelWorker));

private RedisClient sentinelClient;
private RedisClient sentinelPubSubClient;
private PooledRedisClientManager clientsManager;
private IRedisSubscription sentinelSubscription;
private string sentinelName;
private readonly RedisSentinel redisSentinel;
private readonly RedisClient sentinelClient;
private readonly RedisClient sentinelPubSubClient;
private readonly IRedisSubscription sentinelSubscription;
private readonly string sentinelName;
private string host;
private IRedisClientsManager redisManager;

public event EventHandler SentinelError;

public RedisSentinelWorker(string host, string sentinelName, PooledRedisClientManager clientsManager = null)
public RedisSentinelWorker(RedisSentinel redisSentinel, string host, string sentinelName)
{
this.redisSentinel = redisSentinel;
this.redisManager = redisSentinel.redisManager;
this.sentinelName = sentinelName;
this.sentinelClient = new RedisClient(host);
this.sentinelPubSubClient = new RedisClient(host);
this.sentinelSubscription = this.sentinelPubSubClient.CreateSubscription();
this.sentinelSubscription.OnMessage = SentinelMessageReceived;
this.clientsManager = clientsManager;

Log.Info("Set up Redis Sentinel on {0}".Fmt(host));
}
Expand Down Expand Up @@ -81,33 +78,19 @@ private void ConfigureRedisFromSentinel()
var masters = ConvertMasterArrayToList(this.sentinelClient.Sentinel("master", this.sentinelName));
var slaves = ConvertSlaveArrayToList(this.sentinelClient.Sentinel("slaves", this.sentinelName));

if (this.clientsManager == null)
if (redisManager == null)
{
if (slaves.Count() > 0)
{
this.clientsManager = new PooledRedisClientManager(masters, slaves);
}
else
{
this.clientsManager = new PooledRedisClientManager(masters.ToArray());
}
redisManager = redisSentinel.RedisManagerFactory.Create(masters, slaves);
}
else
{
if (slaves.Count() > 0)
{
this.clientsManager.FailoverTo(masters, slaves);
}
else
{
this.clientsManager.FailoverTo(masters.ToArray());
}
((IRedisFailover)redisManager).FailoverTo(masters, slaves);
}
}

private Dictionary<string, string> ParseDataArray(object[] items)
{
Dictionary<string, string> data = new Dictionary<string, string>();
var data = new Dictionary<string, string>();
bool isKey = false;
string key = null;
string value = null;
Expand Down Expand Up @@ -142,7 +125,7 @@ private void ConfigureRedisFromSentinel()
/// </summary>
/// <param name="items"></param>
/// <returns></returns>
private IEnumerable<string> ConvertSlaveArrayToList(object[] slaves)
private List<string> ConvertSlaveArrayToList(object[] slaves)
{
var servers = new List<string>();
string ip = null;
Expand Down Expand Up @@ -176,7 +159,7 @@ private IEnumerable<string> ConvertSlaveArrayToList(object[] slaves)
/// </summary>
/// <param name="items"></param>
/// <returns></returns>
private IEnumerable<string> ConvertMasterArrayToList(object[] items)
private List<string> ConvertMasterArrayToList(object[] items)
{
var servers = new List<string>();
string ip = null;
Expand All @@ -195,11 +178,11 @@ private IEnumerable<string> ConvertMasterArrayToList(object[] items)
return servers;
}

public PooledRedisClientManager GetClientManager()
public IRedisClientsManager GetClientManager()
{
ConfigureRedisFromSentinel();

return this.clientsManager;
return this.redisManager;
}

public void Dispose()
Expand Down
1 change: 1 addition & 0 deletions src/ServiceStack.Redis/ServiceStack.Redis.csproj
Expand Up @@ -151,6 +151,7 @@
<Compile Include="Commands.cs" />
<Compile Include="ConnectionUtils.cs" />
<Compile Include="IHandleClientDispose.cs" />
<Compile Include="RedisManagerFactory.cs" />
<Compile Include="RedisManagerPool.cs" />
<Compile Include="RedisClient_Admin.cs" />
<Compile Include="Generic\ManagedListGeneric.cs" />
Expand Down
35 changes: 27 additions & 8 deletions tests/ServiceStack.Redis.Tests/RedisSentinelTests.cs
@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq;
using System.Text;
using NUnit.Framework;

Expand All @@ -18,7 +16,7 @@ public override void OnBeforeEachTest()
{
base.OnBeforeEachTest();

RedisSentinel = new RedisClient(TestConfig.SingleHost, TestConfig.RedisSentinelPort);
RedisSentinel = new RedisClient(TestConfig.SentinelHost, TestConfig.RedisSentinelPort);
}


Expand Down Expand Up @@ -49,7 +47,7 @@ public void Can_Get_Sentinel_Slaves()
{
object[] slaves = RedisSentinel.Sentinel("slaves", TestConfig.MasterName);

Assert.AreEqual(slaves.Count(), TestConfig.SlaveHosts.Count());
Assert.That(slaves.Count(), Is.GreaterThan(0));
}

[Test]
Expand All @@ -60,23 +58,44 @@ public void Can_Get_Master_Addr()
string host = Encoding.UTF8.GetString((byte[])addr[0]);
string port = Encoding.UTF8.GetString((byte[])addr[1]);

Assert.AreEqual(host, "127.0.0.1"); // IP of localhost
// IP of localhost
Assert.That(host, Is.EqualTo("127.0.0.1").Or.EqualTo(TestConfig.SentinelHost));
Assert.AreEqual(port, TestConfig.RedisPort.ToString());
}

[Test]
public void Can_Get_Redis_ClientsManager()
{
var sentinel = new RedisSentinel(new[] { "{0}:{1}".Fmt(TestConfig.SingleHost, TestConfig.RedisSentinelPort) }, TestConfig.MasterName);
var sentinel = new RedisSentinel(new[] { "{0}:{1}".Fmt(TestConfig.SentinelHost, TestConfig.RedisSentinelPort) }, TestConfig.MasterName);

var clientsManager = sentinel.Setup();
var client = clientsManager.GetClient();

Assert.AreEqual(client.Host, "127.0.0.1");
Assert.That(client.Host, Is.EqualTo("127.0.0.1").Or.EqualTo(TestConfig.SentinelHost));
Assert.AreEqual(client.Port, TestConfig.RedisPort);

client.Dispose();
sentinel.Dispose();
}

[Test]
public void Can_specify_Timeout_on_RedisManager()
{
var sentinel = new RedisSentinel(new[] { "{0}:{1}".Fmt(TestConfig.SentinelHost, TestConfig.RedisSentinelPort) }, TestConfig.MasterName)
{
RedisManagerFactory = {
OnInit = r => {
((PooledRedisClientManager)r).IdleTimeOutSecs = 20;
}
}
};

using (var clientsManager = (PooledRedisClientManager)sentinel.Setup())
using (var client = clientsManager.GetClient())
{
Assert.That(clientsManager.IdleTimeOutSecs, Is.EqualTo(20));
Assert.That(((RedisNativeClient)client).IdleTimeOutSecs, Is.EqualTo(20));
}
}
}
}
1 change: 1 addition & 0 deletions tests/ServiceStack.Redis.Tests/TestConfig.cs
Expand Up @@ -13,6 +13,7 @@ static TestConfig()
public const bool IgnoreLongTests = true;

public const string SingleHost = "localhost";
public const string SentinelHost = "10.0.0.9";
public const string MasterName = "mymaster";
public static readonly string[] MasterHosts = new[] { "localhost" };
public static readonly string[] SlaveHosts = new[] { "localhost" };
Expand Down

0 comments on commit 7912833

Please sign in to comment.