Commits

Andriy Kornatskyy committed 9ab5220

Added one_pass_create and get_or_create cache patterns.

Comments (0)

Files changed (2)

src/wheezy/caching/patterns.py

     return result
 
 
+def one_pass_create(key, create_factory, dependency_factory=None,
+                    time=0, namespace=None, cache=None,
+                    timeout=10, key_prefix='one_pass:'):
+    """ Cache Pattern: try enter one pass: (1) if entered
+        use *create_factory* to get a value if result is not `None`
+        use cache `set` operation to store result and use
+        *dependency_factory* to get an instance of `CacheDependency`
+        to add *key* to it; (2) if not entered `wait` until one
+        pass is available and it is not timed out get an item by *key*
+        from *cache*.
+    """
+    result = None
+    one_pass = OnePass(cache, key_prefix + key, timeout, namespace)
+    try:
+        one_pass.__enter__()
+        if one_pass.acquired:
+            result = create_factory()
+            if result is not None:
+                cache.set(key, result, time, namespace)
+                if dependency_factory is not None:
+                    dependency = dependency_factory()
+                    dependency.add(key, namespace)
+        elif one_pass.wait():
+            result = cache.get(key, namespace)
+    finally:
+        one_pass.__exit__(None, None, None)
+    return result
+
+
+def get_or_create(key, create_factory, dependency_factory=None,
+                  time=0, namespace=None, cache=None,
+                  timeout=10, key_prefix='one_pass:'):
+    """ Cache Pattern: get an item by *key* from *cache* and
+        if it is not available see `one_pass_create`.
+    """
+    result = cache.get(key, namespace)
+    if result is not None:
+        return result
+    return one_pass_create(key, create_factory, dependency_factory,
+                           time, namespace, cache, timeout, key_prefix)
+
+
 class OnePass(object):
     """ A solution to `Thundering Head` problem.
 

src/wheezy/caching/tests/test_patterns.py

         self.mock_cache.set.assert_called_once_with('key', 'x', 10, 'ns')
         mock_dependency_factory.return_value.add.assert_called_once_with(
             'key', 'ns')
+
+
+class OnePassCreateTestCase(unittest.TestCase):
+
+    def setUp(self):
+        self.mock_cache = Mock()
+        self.mock_create_factory = Mock()
+
+    def one_pass_create(self, dependency_factory=None):
+        from wheezy.caching.patterns import one_pass_create as opc
+        return opc('key', self.mock_create_factory, dependency_factory,
+                   10, 'ns', self.mock_cache, 5)
+
+    def test_create_none(self):
+        """ One pass has been entered and create factory returns None.
+        """
+        self.mock_cache.add.return_value = True
+        self.mock_create_factory.return_value = None
+        assert not self.one_pass_create()
+
+        self.mock_cache.add.assert_called_once_with(
+            'one_pass:key', ANY, 5, 'ns')
+        self.mock_create_factory.assert_called_once_with()
+        assert not self.mock_cache.set.called
+        self.mock_cache.delete.assert_called_once_with('one_pass:key', 'ns')
+
+    def test_no_dependency(self):
+        """ Create factory returned value.
+        """
+        self.mock_cache.add.return_value = True
+        self.mock_create_factory.return_value = 'x'
+        assert 'x' == self.one_pass_create()
+
+        self.mock_cache.add.assert_called_once_with(
+            'one_pass:key', ANY, 5, 'ns')
+        self.mock_create_factory.assert_called_once_with()
+        self.mock_cache.set.assert_called_once_with('key', 'x', 10, 'ns')
+        self.mock_cache.delete.assert_called_once_with('one_pass:key', 'ns')
+
+    def test_with_dependency(self):
+        """ Create factory returned value.
+        """
+        self.mock_cache.add.return_value = True
+        self.mock_create_factory.return_value = 'x'
+        mock_dependency_factory = Mock()
+        assert 'x' == self.one_pass_create(mock_dependency_factory)
+
+        self.mock_cache.add.assert_called_once_with(
+            'one_pass:key', ANY, 5, 'ns')
+        self.mock_create_factory.assert_called_once_with()
+        self.mock_cache.set.assert_called_once_with('key', 'x', 10, 'ns')
+        self.mock_cache.delete.assert_called_once_with('one_pass:key', 'ns')
+        mock_dependency_factory.return_value.add.assert_called_once_with(
+            'key', 'ns')
+
+    @patch('wheezy.caching.patterns.OnePass')
+    def test_wait_timedout(self, mock_cls_one_pass):
+        """ Wait on one pass has timed out.
+        """
+        mock_one_pass = mock_cls_one_pass.return_value
+        self.mock_cache.add.return_value = False
+        mock_one_pass.acquired = False
+        mock_one_pass.wait.return_value = False
+
+        assert not self.one_pass_create()
+
+    @patch('wheezy.caching.patterns.OnePass')
+    def test_wait_get(self, mock_cls_one_pass):
+        """ Wait on one pass succeed, get value.
+        """
+        mock_one_pass = mock_cls_one_pass.return_value
+        self.mock_cache.add.return_value = False
+        mock_one_pass.acquired = False
+        mock_one_pass.wait.return_value = True
+        self.mock_cache.get.return_value = 'x'
+
+        assert 'x' == self.one_pass_create()
+
+
+class GetOrCreateTestCase(unittest.TestCase):
+
+    def setUp(self):
+        self.mock_cache = Mock()
+        self.mock_create_factory = Mock()
+
+    def get_or_create(self):
+        from wheezy.caching.patterns import get_or_create as gc
+        return gc('key', self.mock_create_factory, None,
+                  10, 'ns', self.mock_cache, 5)
+
+    def test_found(self):
+        """ An item found in cache.
+        """
+        self.mock_cache.get.return_value = 'x'
+        assert 'x' == self.get_or_create()
+        self.mock_cache.get.assert_called_once_with('key', 'ns')
+        assert not self.mock_create_factory.called
+
+    def test_not_found(self):
+        """ Not found in cache.
+        """
+        self.mock_cache.get.return_value = None
+        self.mock_create_factory.return_value = 'x'
+        assert 'x' == self.get_or_create()
+        self.mock_cache.get.assert_called_once_with('key', 'ns')