1313from invocation import HandlerResult
1414
1515
16- def request_digest (payload : dict ) -> bytes :
16+ def request_digest (payload : dict , * , execution_digest : str | None = None ) -> bytes :
17+ canonical = json .dumps (
18+ {"schema_version" : "2.0" , "execution_digest" : execution_digest ,
19+ "payload" : payload }, sort_keys = True , separators = (',' , ':' ))
20+ return hashlib .sha256 (canonical .encode ()).digest ()
21+
22+
23+ def legacy_request_digest (payload : dict ) -> bytes :
1724 canonical = json .dumps (payload , sort_keys = True , separators = (',' , ':' ))
1825 return hashlib .sha256 (canonical .encode ()).digest ()
1926
@@ -24,9 +31,11 @@ class CompositeCache:
2431 def __init__ (self , * repositories : Any ) -> None :
2532 self .repositories = tuple (r for r in repositories if r is not None )
2633
27- def lookup (self , method_id : str , payload : dict ) -> HandlerResult | None :
34+ def lookup (self , method_id : str , payload : dict , * ,
35+ execution_digest : str | None = None ) -> HandlerResult | None :
2836 for repository in self .repositories :
29- hit = repository .lookup (method_id , payload )
37+ hit = repository .lookup (method_id , payload ,
38+ execution_digest = execution_digest )
3039 if hit is not None :
3140 return hit
3241 return None
@@ -46,7 +55,8 @@ def __init__(self, connect, artifact_store, *, excluded_methods=()) -> None:
4655 self .counters = {'looked_up' : 0 , 'hit' : 0 , 'miss' : 0 ,
4756 'stored' : 0 , 'deduplicated' : 0 }
4857
49- def lookup (self , method_id : str , payload : dict ) -> HandlerResult | None :
58+ def lookup (self , method_id : str , payload : dict , * ,
59+ execution_digest : str | None = None ) -> HandlerResult | None :
5060 if method_id in self ._excluded :
5161 return None
5262 self .counters ['looked_up' ] += 1
@@ -56,8 +66,19 @@ def lookup(self, method_id: str, payload: dict) -> HandlerResult | None:
5666 'FROM app.v_result_cache_servable c '
5767 'JOIN meta.method m ON m.id = c.method_row_id '
5868 'WHERE m.method_id = %s AND c.request_digest = %s' ,
59- (method_id , request_digest (payload )))
69+ (method_id , request_digest (payload , execution_digest = execution_digest )))
6070 row = cur .fetchone ()
71+ if row is None and execution_digest is not None and '.motif.' not in method_id :
72+ # Compatibility window for results written before cache identity v2.
73+ # Motif never falls back because checkpoint/calibration/runtime identity
74+ # is part of correctness, not only freshness.
75+ cur .execute (
76+ 'SELECT c.id, c.result, c.provenance, c.warnings, c.parameters_used '
77+ 'FROM app.v_result_cache_servable c '
78+ 'JOIN meta.method m ON m.id = c.method_row_id '
79+ 'WHERE m.method_id = %s AND c.request_digest = %s' ,
80+ (method_id , legacy_request_digest (payload )))
81+ row = cur .fetchone ()
6182 if row is None :
6283 self .counters ['miss' ] += 1
6384 return None
@@ -78,7 +99,8 @@ def lookup(self, method_id: str, payload: dict) -> HandlerResult | None:
7899
79100 def store (self , method_id : str , payload : dict , out : HandlerResult , * ,
80101 seconds : float , job_id : str | None = None ,
81- envelope : dict | None = None ) -> None :
102+ envelope : dict | None = None ,
103+ execution_digest : str | None = None ) -> None :
82104 if method_id in self ._excluded or out .cache != 'computed' or envelope is None :
83105 return
84106 references = list (envelope .get ('artifacts' ) or [])
@@ -94,7 +116,7 @@ def store(self, method_id: str, payload: dict, out: HandlerResult, *,
94116 ' parameters_used, source_job_id, compute_seconds) '
95117 'VALUES (%s,%s,%s,%s,%s,%s,%s,%s) '
96118 'ON CONFLICT (method_row_id, request_digest) DO NOTHING RETURNING id' ,
97- (method [0 ], request_digest (payload ), json .dumps (out .result ),
119+ (method [0 ], request_digest (payload , execution_digest = execution_digest ), json .dumps (out .result ),
98120 json .dumps (out .provenance ), json .dumps (out .warnings ),
99121 json .dumps (out .parameters_used ), job_id , seconds ))
100122 inserted = cur .fetchone ()
0 commit comments