| 
					
				 | 
			
			
				@@ -24,24 +24,33 @@ from . import base 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     import redis 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     from kombu.transport.redis import get_redis_error_classes 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-except ImportError:                 # pragma: no cover 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    redis = None                    # noqa 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+except ImportError:  # pragma: no cover 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    redis = None  # noqa 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     get_redis_error_classes = None  # noqa 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-__all__ = ('RedisBackend',) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    from redis import sentinel 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+except ImportError: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    sentinel = None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+__all__ = ('RedisBackend', 'SentinelBackend') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 E_REDIS_MISSING = """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 You need to install the redis library in order to use \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 the Redis result store backend. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+E_REDIS_SENTINEL_MISSING = """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+You need to install the redis library with support of \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+sentinel in order to use the Redis result store backend. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 E_LOST = 'Connection to Redis lost: Retry (%s/%s) %s.' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 logger = get_logger(__name__) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 class ResultConsumer(async.BaseResultConsumer): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     _pubsub = None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def __init__(self, *args, **kwargs): 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -265,12 +274,12 @@ class RedisBackend(base.BaseKeyValueStoreBackend, async.AsyncBackendMixin): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         tkey = self.get_key_for_group(gid, '.t') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         result = self.encode_result(result, state) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         with client.pipeline() as pipe: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            _, readycount, totaldiff, _, _ = pipe                           \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                .rpush(jkey, self.encode([1, tid, state, result]))          \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                .llen(jkey)                                                 \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                .get(tkey)                                                  \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                .expire(jkey, self.expires)                                 \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                .expire(tkey, self.expires)                                 \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            _, readycount, totaldiff, _, _ = pipe \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .rpush(jkey, self.encode([1, tid, state, result])) \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .llen(jkey) \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .get(tkey) \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .expire(jkey, self.expires) \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .expire(tkey, self.expires) \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 .execute() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         totaldiff = int(totaldiff or 0) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -281,10 +290,10 @@ class RedisBackend(base.BaseKeyValueStoreBackend, async.AsyncBackendMixin): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if readycount == total: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 decode, unpack = self.decode, self._unpack_chord_result 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 with client.pipeline() as pipe: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    resl, _, _ = pipe               \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        .lrange(jkey, 0, total)     \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        .delete(jkey)               \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        .delete(tkey)               \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    resl, _, _ = pipe \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        .lrange(jkey, 0, total) \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        .delete(jkey) \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        .delete(tkey) \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         .execute() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     callback.delay([unpack(tup, decode) for tup in resl]) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -306,10 +315,16 @@ class RedisBackend(base.BaseKeyValueStoreBackend, async.AsyncBackendMixin): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def _create_client(self, **params): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return self.redis.StrictRedis( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            connection_pool=self.ConnectionPool(**params), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._get_client()( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            connection_pool=self._get_pool(**params), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def _get_client(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self.redis.StrictRedis 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def _get_pool(self, **params): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self.ConnectionPool(**params) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def ConnectionPool(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if self._ConnectionPool is None: 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -340,3 +355,63 @@ class RedisBackend(base.BaseKeyValueStoreBackend, async.AsyncBackendMixin): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @deprecated.Property(4.0, 5.0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def password(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         return self.connparams['password'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class SentinelBackend(RedisBackend): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    """Redis sentinel task result store.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    sentinel = sentinel 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def __init__(self, *args, **kwargs): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if self.sentinel is None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            raise ImproperlyConfigured(E_REDIS_SENTINEL_MISSING.strip()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        super(SentinelBackend, self).__init__(*args, **kwargs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def _params_from_url(self, url, defaults): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # URL looks like sentinel://0.0.0.0:26347/3;sentinel://0.0.0.0:26348/3. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        chunks = url.split(";") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        connparams = dict(defaults, hosts=[]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for chunk in chunks: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data = super(SentinelBackend, self)._params_from_url( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                url=chunk, defaults=defaults) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            connparams['hosts'].append(data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for p in ("host", "port", "db", "password"): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            connparams.pop(p) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # Adding db/password in connparams to connect to the correct instance 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for p in ("db", "password"): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if connparams['hosts'] and p in connparams['hosts'][0]: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                connparams[p] = connparams['hosts'][0].get(p) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return connparams 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def _get_sentinel_instance(self, **params): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        connparams = params.copy() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        hosts = connparams.pop("hosts") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        result_backend_transport_opts = self.app.conf.get( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "result_backend_transport_options", {}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        min_other_sentinels = result_backend_transport_opts.get( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "min_other_sentinels", 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        sentinel_kwargs = result_backend_transport_opts.get( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "sentinel_kwargs", {}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        sentinel_instance = self.sentinel.Sentinel( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            [(cp['host'], cp['port']) for cp in hosts], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            min_other_sentinels=min_other_sentinels, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            sentinel_kwargs=sentinel_kwargs, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            **connparams) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return sentinel_instance 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def _get_pool(self, **params): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        sentinel_instance = self._get_sentinel_instance(**params) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        result_backend_transport_opts = self.app.conf.get( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "result_backend_transport_options", {}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        master_name = result_backend_transport_opts.get("master_name", None) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return sentinel_instance.master_for( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            service_name=master_name, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            redis_class=self._get_client(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ).connection_pool 
			 |