sqlalchemy / sqlalchemy/dogpile.cache

exposing a region's configuration/lock for custom functionality

Open
#125 10 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

feature low priority
Dominant language
Python
Stars
299
Forks
50
PR merge metrics
No merged PRs in 30d

Description

Migrated issue, originally created by jvanasco (jvanasco)

with some advanced Redis usage, I need to handle caching certain keys in ways that are incompatible with dogpile.cache as-is, and must use the dogpile.core lock directly. at the same time, I still want to leverage my dogpile region and it's configuration.

by "incompatible", i mean various mechanisms that can't be fixed with a custom backend or Proxy. In one example, a key corresponds to a redis hash with messages for a user, and contains at least 3 values (count unread, count read, cached_ids):

messages|{user_id} count_read  = 10
messages|{user_id} count_unread = 5
messages|{user_id} cached_ids = (1,2,3,4,5,6,7,8,9,10)
messages|{user_id} msg-1 = {foo: bar}
messages|{user_id} msg-2 = {foo: bar}
messages|{user_id} msg-3 = {foo: bar}

getting/updating the message counts requires writing/validating two keys at once (count_read, count_unread). various functions need to happen within a redis pipeline as well.

to handle this sort of operation, I am essentially leveraging the code below - which is based on the getcreate logic

would it make sense to integrate something like this into the CacheRegion itself?

#!python

    import logging
    log = logging.getLogger(__name__)


    from dogpile import Lock, NeedRegenerationException
    from dogpile.cache.api import NO_VALUE


def raw_getcreate(region, key, f_get, f_gen, f_async=None):
    # key is needed for the lock

	def _get_value():
		if __debug__:
			log.debug("raw_getcreate._get_value")
		value = f_get()
		if value in (None, NO_VALUE):
			raise NeedRegenerationException()
		return value, -1

	def _gen_value():
		if __debug__:
			log.debug("raw_getcreate._gen_value")
		try:
			if __debug__:
				log.debug("raw_getcreate._gen_value | first, try fetching")
			return _get_value()
		except NeedRegenerationException as e:
			pass
		if __debug__:
			log.debug("raw_getcreate._gen_value | create")
		created_value = f_gen()
		return created_value, -1
			
	with Lock(
			region._mutex(key),
			_gen_value,
			_get_value,
			None,  # expiration_time, never expire
			f_async,  # async creator, unused
	) as value:
		return value


reg = CACHE_REGIONS['foo']
key = "testkey-2"

def f_get():
	value = reg.backend_actual.get(key)
	return value

def f_gen():
	created_value = "NEWDATA"
	uploaded = reg.backend_actual.set(key, created_value)
	return created_value

print raw_getcreate(reg, key, f_get, f_gen)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading CacheRegion and the region._mutex and backend_actual entry points shown in the issue, then compare them with the direct dogpile Lock usage in raw_getcreate. Done would be a defined CacheRegion interface that exposes the needed configuration or lock behavior for custom Redis operations without requiring unsupported internals.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, redis
Domain
backend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.