Normalize recoverable rejection acknowledgements

This commit is contained in:
fy59 2026-09-18 23:25:43 +02:00
parent 59673a0739
commit 457abc0a51
3 changed files with 22 additions and 5 deletions

View file

@ -23,14 +23,18 @@ class SyncErrorClassificationTest(unittest.TestCase):
"generation_id TEXT,producer_peer_id TEXT,consumer_peer_id TEXT,"
"consumed_at TEXT,result TEXT,ack_json TEXT)"
)
for generation, result in (("gen_consumed", "consumed"),
("gen_rejected", "rejected"),
("gen_other", "consumed")):
for generation, result, diagnostic in (
("gen_consumed", "consumed", ""),
("gen_rejected", "rejected", "bounded rejection"),
("gen_legacy", "rejected", "legacy/path"),
("gen_other", "consumed", ""),
):
database.execute(
"INSERT INTO sync_consumed_generations VALUES(?,?,?,?,?,?)",
(generation, "android" if generation != "gen_other" else "other",
"desktop", generation, result,
json.dumps({"generation_id": generation, "result": result})),
json.dumps({"generation_id": generation, "result": result,
"diagnostic": diagnostic})),
)
acknowledgements = sync_peer_worker.recovery_acknowledgements(

View file

@ -566,6 +566,11 @@ def record_consumed(db: sqlite3.Connection, manifest: dict, manifest_digest: str
def record_rejected(database: Path, manifest: dict, manifest_digest: str, diagnostic: str) -> dict:
# Android's platform JSON quoter escapes solidus characters while the
# desktop canonical encoder does not. Rejection diagnostics are bounded
# presentation text, so normalize that separator before hashing the ACK;
# identities and protocol fields remain untouched.
diagnostic = diagnostic.replace("/", ":")
diagnostic = diagnostic.encode("utf-8")[:MAX_DIAGNOSTIC].decode("utf-8", "ignore")
with closing(connect_database(database)) as db:
require_schema(db); db.execute("BEGIN IMMEDIATE")

View file

@ -75,7 +75,15 @@ def recovery_acknowledgements(db, producer_peer_id: str, consumer_peer_id: str)
"ORDER BY consumed_at,generation_id LIMIT 32",
(producer_peer_id, consumer_peer_id),
).fetchall()
return [json.loads(row[0]) for row in rows]
acknowledgements = [json.loads(row[0]) for row in rows]
# ACKs created before rejection diagnostics were normalized may contain a
# solidus that Android's platform JSON canonicalizer hashes differently.
# They remain durable evidence but cannot close a peer row, so do not let
# one legacy object block recovery of later compatible conversations.
return [
value for value in acknowledgements
if value.get("result") != "rejected" or "/" not in value.get("diagnostic", "")
]
def run_adapter(