/// <inheritdoc /> public void FailoverTo(params string[] readWriteHosts) { Interlocked.Increment(ref RedisState.TotalFailovers); lock (this._clients) { for (var i = 0; i < this._clients.Length; i++) { var redis = this._clients[i]; if (redis != null) { RedisState.DeactivateClient(redis); } this._clients[i] = null; } this.RedisResolver.ResetMasters(readWriteHosts); } if (this.OnFailover != null) { foreach (var callback in this.OnFailover) { try { callback(this); } catch (Exception ex) { _logger.Error("Error firing OnFailover callback(): ", ex); } } } }
protected virtual void Dispose(bool disposing) { if (Interlocked.Increment(ref this._disposeAttempts) > 1) { return; } if (disposing) { // get rid of managed resources } try { // get rid of unmanaged resources foreach (var t in this._writeClients) { this.Dispose(t); } foreach (var t in this._readClients) { this.Dispose(t); } } catch (Exception ex) { _logger.Error("Error when trying to dispose of PooledRedisClientManager", ex); } RedisState.DisposeAllDeactivatedClients(); }
/// <summary> /// Returns a Read/Write client (The default) using the hosts defined in ReadWriteHosts /// </summary> public IRedisClient GetClient() { try { int inactivePoolIndex; lock (this._clients) { this.AssertValidPool(); // -1 when no available clients otherwise index of reservedSlot or existing Client inactivePoolIndex = this.GetInActiveClient(out var inActiveClient); // inActiveClient != null only for Valid InActive Clients if (inActiveClient != null) { this.PoolIndex++; inActiveClient.Active = true; return(!this.AssertAccessOnlyOnSameThread ? inActiveClient : inActiveClient.LimitAccessToThread(Thread.CurrentThread.ManagedThreadId, Environment.StackTrace)); } } // Reaches here when there's no Valid InActive Clients try { // inactivePoolIndex == -1 || index of reservedSlot || index of invalid client var existingClient = inactivePoolIndex >= 0 && inactivePoolIndex < this._clients.Length ? this._clients[inactivePoolIndex] : null; if (existingClient != null && existingClient != _reservedSlot && existingClient.HadExceptions) { RedisState.DeactivateClient(existingClient); } var newClient = this.InitNewClient(this.RedisResolver.CreateMasterClient(Math.Max(inactivePoolIndex, 0))); // Put all blocking I/O or potential Exceptions before lock lock (this._clients) { // Create new client outside of pool when max pool size exceeded // Reverting free-slot not needed when -1 since slot wasn't reserved or // when existingClient changed (failover) since no longer reserved var stillReserved = inactivePoolIndex >= 0 && inactivePoolIndex < this._clients.Length && this._clients[inactivePoolIndex] == existingClient; if (inactivePoolIndex == -1 || !stillReserved) { if (_logger.IsDebugEnabled()) { _logger.Debug(string.Format("clients[inactivePoolIndex] != existingClient: {0}", !stillReserved ? "!stillReserved" : "-1")); } Interlocked.Increment(ref RedisState.TotalClientsCreatedOutsidePool); // Don't handle callbacks for new client outside pool newClient.ClientManager = null; return(newClient); } this.PoolIndex++; this._clients[inactivePoolIndex] = newClient; return(!this.AssertAccessOnlyOnSameThread ? newClient : newClient.LimitAccessToThread(Thread.CurrentThread.ManagedThreadId, Environment.StackTrace)); } } catch { // Revert free-slot for any I/O exceptions that can throw (before lock) lock (this._clients) { if (inactivePoolIndex >= 0 && inactivePoolIndex < this._clients.Length) { this._clients[inactivePoolIndex] = null; } } throw; } } finally { RedisState.DisposeExpiredClients(); } }
/// <summary> /// Returns a ReadOnly client using the hosts defined in ReadOnlyHosts. /// </summary> public virtual IRedisClient GetReadOnlyClient() { try { var poolTimedOut = false; int inactivePoolIndex; lock (this._readClients) { this.AssertValidReadOnlyPool(); RedisClient inActiveClient; while ((inactivePoolIndex = this.GetInActiveReadClient(out inActiveClient)) == -1) { if (this.PoolTimeout.HasValue) { // wait for a connection, break out if made to wait too long if (!Monitor.Wait(this._readClients, this.PoolTimeout.Value)) { poolTimedOut = true; break; } } else { Monitor.Wait(this._readClients, this.RecheckPoolAfterMs); } } // inActiveClient != null only for Valid InActive Clients if (inActiveClient != null) { this.ReadPoolIndex++; inActiveClient.Active = true; this.InitClient(inActiveClient); return(inActiveClient); } } if (poolTimedOut) { throw new TimeoutException(_poolTimeoutError); } // Reaches here when there's no Valid InActive Clients try { // inactivePoolIndex = index of reservedSlot || index of invalid client var existingClient = this._readClients[inactivePoolIndex]; if (existingClient != null && existingClient != _reservedSlot && existingClient.HadExceptions) { RedisState.DeactivateClient(existingClient); } var newClient = this.InitNewClient(this.RedisResolver.CreateSlaveClient(inactivePoolIndex)); // Put all blocking I/O or potential Exceptions before lock lock (this._readClients) { // If existingClient at inactivePoolIndex changed (failover) return new client outside of pool if (this._readClients[inactivePoolIndex] != existingClient) { if (_logger.IsDebugEnabled()) { _logger.Debug(string.Format("readClients[inactivePoolIndex] != existingClient: {0}", this._readClients[inactivePoolIndex])); } Interlocked.Increment(ref RedisState.TotalClientsCreatedOutsidePool); // Don't handle callbacks for new client outside pool newClient.ClientManager = null; return(newClient); // return client outside of pool } this.ReadPoolIndex++; this._readClients[inactivePoolIndex] = newClient; return(newClient); } } catch { // Revert free-slot for any I/O exceptions that can throw lock (this._readClients) { this._readClients[inactivePoolIndex] = null; // free slot } throw; } } finally { RedisState.DisposeExpiredClients(); } }
/// <summary> /// Returns a Read/Write client (The default) using the hosts defined in ReadWriteHosts /// </summary> /// <inheritdoc /> public IRedisClient GetClient() { try { var poolTimedOut = false; int inactivePoolIndex; lock (this._writeClients) { this.AssertValidReadWritePool(); RedisClient inActiveClient; while ((inactivePoolIndex = this.GetInActiveWriteClient(out inActiveClient)) == -1) { if (this.PoolTimeout.HasValue) { // wait for a connection, cry out if made to wait too long if (!Monitor.Wait(this._writeClients, this.PoolTimeout.Value)) { poolTimedOut = true; break; } } else { Monitor.Wait(this._writeClients, this.RecheckPoolAfterMs); } } // inActiveClient != null only for Valid InActive Clients if (inActiveClient != null) { this.WritePoolIndex++; inActiveClient.Active = true; this.InitClient(inActiveClient); return(!this.AssertAccessOnlyOnSameThread ? inActiveClient : inActiveClient.LimitAccessToThread(Thread.CurrentThread.ManagedThreadId, Environment.StackTrace)); } } if (poolTimedOut) { throw new TimeoutException(_poolTimeoutError); } // Reaches here when there's no Valid InActive Clients try { // inactivePoolIndex = index of reservedSlot || index of invalid client var existingClient = this._writeClients[inactivePoolIndex]; if (existingClient != null && existingClient != _reservedSlot && existingClient.HadExceptions) { RedisState.DeactivateClient(existingClient); } var newClient = this.InitNewClient(this.RedisResolver.CreateMasterClient(inactivePoolIndex)); // Put all blocking I/O or potential Exceptions before lock lock (this._writeClients) { // If existingClient at inactivePoolIndex changed (failover) return new client outside of pool if (this._writeClients[inactivePoolIndex] != existingClient) { if (_logger.IsDebugEnabled()) { _logger.Debug( string.Format("writeClients[inactivePoolIndex] != existingClient: {0}", this._writeClients[inactivePoolIndex])); } return(newClient); // return client outside of pool } this.WritePoolIndex++; this._writeClients[inactivePoolIndex] = newClient; return(!this.AssertAccessOnlyOnSameThread ? newClient : newClient.LimitAccessToThread(Thread.CurrentThread.ManagedThreadId, Environment.StackTrace)); } } catch { // Revert free-slot for any I/O exceptions that can throw (before lock) lock (this._writeClients) { this._writeClients[inactivePoolIndex] = null; // free slot } throw; } } finally { RedisState.DisposeExpiredClients(); } }