11import datetime
2- from typing import (
3- Dict ,
4- Optional ,
5- Protocol ,
6- Sequence ,
7- Tuple ,
8- Union ,
9- cast ,
10- )
2+ from functools import wraps
3+ from typing import Dict , Optional , Protocol , Sequence , Tuple , Union , cast
114
125import fakeredis
136import redis
147
158from corva import cache_adapter
16- from corva .cache_adapter import HashMigrator
179
1810
1911class UserCacheSdkProtocol (Protocol ):
@@ -62,18 +54,57 @@ def __init__(
6254 elif redis_client is None :
6355 redis_client = redis .Redis .from_url (url = redis_dsn , decode_responses = True )
6456
65- migrator = HashMigrator (hash_name , redis_client )
66- migrator .run ()
67- hash_name = HashMigrator .NEW_HASH_PREFIX + hash_name
57+ # Lazy migration: do not run on init; defer until first read/write/delete
58+ self ._original_hash_name = hash_name
59+ self ._redis_client = cast (redis .Redis , redis_client )
60+ self ._migrated = False
6861
6962 self .cache_repo = cache_adapter .RedisRepository (
70- hash_name = hash_name ,
71- client = cast ( redis . Redis , redis_client ) ,
63+ hash_name = cache_adapter . HashMigrator . NEW_HASH_PREFIX + hash_name ,
64+ client = self . _redis_client ,
7265 )
7366
67+ @staticmethod
68+ def ensure_migrated_once (method ):
69+ """
70+ A static method decorator that ensures a specific migration process
71+ has been executed before invoking the decorated method. Once the
72+ migration is attempted, the decorator marks it as complete,
73+ regardless of the outcome, to optimize subsequent calls.
74+
75+ Args:
76+ method (Callable): The `UserRedisSdk.method` to be decorated.
77+
78+ Returns:
79+ Callable: The wrapped method which ensures that the migration process
80+ has been attempted before execution.
81+ """
82+
83+ @wraps (method )
84+ def wrapper (self , * args , ** kwargs ):
85+
86+ if not self ._migrated :
87+ migrator = cache_adapter .HashMigrator (
88+ hash_name = self ._original_hash_name ,
89+ client = self ._redis_client ,
90+ )
91+ try :
92+ migrator .run ()
93+ finally :
94+ # Regardless of outcome (True/False), mark as attempted to avoid
95+ # repeating the check on every call. Subsequent calls operate on
96+ # the new-hash namespace.
97+ self ._migrated = True
98+
99+ return method (self , * args , ** kwargs )
100+
101+ return wrapper
102+
103+ @ensure_migrated_once
74104 def set (self , key : str , value : str , ttl : int = SIXTY_DAYS ) -> None :
75105 self .cache_repo .set (key = key , value = value , ttl = ttl )
76106
107+ @ensure_migrated_once
77108 def set_many (
78109 self , data : Sequence [Union [Tuple [str , str ], Tuple [str , str , int ]]]
79110 ) -> None :
@@ -85,20 +116,26 @@ def set_many(
85116 ]
86117 self .cache_repo .set_many (data = prepared_data )
87118
119+ @ensure_migrated_once
88120 def get (self , key : str ) -> Optional [str ]:
89121 return self .cache_repo .get (key = key )
90122
123+ @ensure_migrated_once
91124 def get_many (self , keys : Sequence [str ]) -> Dict [str , Optional [str ]]:
92125 return self .cache_repo .get_many (keys = keys )
93126
127+ @ensure_migrated_once
94128 def get_all (self ) -> Dict [str , str ]:
95129 return self .cache_repo .get_all ()
96130
131+ @ensure_migrated_once
97132 def delete (self , * , key : str ) -> None :
98133 self .cache_repo .delete (key = key )
99134
135+ @ensure_migrated_once
100136 def delete_many (self , keys : Sequence [str ]) -> None :
101137 self .cache_repo .delete_many (keys = keys )
102138
139+ @ensure_migrated_once
103140 def delete_all (self ) -> None :
104141 self .cache_repo .delete_all ()
0 commit comments