text: """Prevents inconsistent db updates for flow."""
  from __future__ import unicode_literals
  
  import time
  import random
  from contextlib import contextmanager
  
  from django.core.cache import cache as default_cache
  from django.db import transaction, DatabaseError
  
  from viewflow.exceptions import FlowLockFailed
  
  
  class NoLock(object):
      """
      No pessimistic locking, just execute flow task in transaction.
  
      Not suitable when you have Join nodes in your flow.
      """
  
      def __call__(self, flow):
          @contextmanager
          def lock(flow_class, process_pk):
              with transaction.atomic():
                  yield
          return lock
  
  
  class SelectForUpdateLock(object):
      """
      Database lock uses `select ... for update` on the process instance row.
  
      Recommended to use with PostgreSQL.
      """
      def __init__(self, nowait=True, attempts=5):
          self.nowait = nowait
          self.attempts = attempts
  
      def __call__(self, flow):
          @contextmanager
          def lock(flow_class, process_pk):
              for i in range(self.attempts):
                  with transaction.atomic():
                      try:
                          process = flow_class.process_class._default_manager.filter(pk=process_pk)
                          process.select_for_update(nowait=self.nowait).exists()
                      except DatabaseError:
                          if i != self.attempts - 1:
                              sleep_time = (((i + 1) * random.random()) + 2 ** i) / 2.5
                              time.sleep(sleep_time)
                          else:
                              raise FlowLockFailed('Lock failed for {}'.format(flow_class))
                      else:
                          yield
                          break
          return lock
  
  
  class CacheLock(object):
      """
      Task lock based on Django cache.
  
      Use it if primary cache backend has transactional `add` functionality,
      like `memcached`.
  
      Example::
  
          class MyFlow(Flow):
              lock_impl = CacheLock(cache=caches['locks'])
  
      The example uses a different cache. The default cache
      is Django ``default`` cache configuration.
      """
  
      def __init__(self, cache=default_cache, attempts=5, expires=120):  # noqa D102
          self.cache = cache
          self.attempts = attempts
          self.expires = expires
  
      def __call__(self, flow):  # noqa D102
          @contextmanager
          def lock(flow_class, process_pk):
              key = 'django-viewflow-lock-{}/{}'.format(flow_class._meta.flow_label, process_pk)
  
              for i in range(self.attempts):
                  if self.cache.add(key, 1, self.expires):
                      break
                  if i != self.attempts - 1:
                      sleep_time = (((i + 1) * random.random()) + 2 ** i) / 2.5
                      time.sleep(sleep_time)
              else:
                  raise FlowLockFailed('Lock failed for {}'.format(flow_class))
  
              try:
                  with transaction.atomic():
                      yield
              finally:
                  self.cache.delete(key)
  
          return lock
  
  
  no_lock = NoLock()
  cache_lock = CacheLock()
  select_for_update_lock = SelectForUpdateLock()
  