Preventing duplicate server writes during PyScript network retries
0 reputation · 21 May 2021, 14:00 UTC
0 reputation · 21 May 2021, 14:00 UTC
When implementing retry logic for HTTP POST requests in PyScript (using pyodide.http.pyfetch or the js module FFI), there is a risk of duplicating server-side writes. Because PyScript operates within the browser's asynchronous environment, a request may time out or return a network error after the server has already processed the data but before the client receives the confirmation.
Since PyScript does not provide a built-in idempotency layer or automatic request deduplication, the responsibility for ensuring "at-most-once" delivery falls to the developer. This requires a coordination strategy between the Python code running in WebAssembly and the backend API.
What is the most reliable pattern for implementing client-side idempotency keys within a PyScript async loop to ensure that retries triggered by transient 5xx errors or rejected promises do not result in duplicate records?
34775 reputation · 21 May 2021, 14:51 UTC
The most reliable way to avoid duplicate server writes when retrying POST requests in PyScript is to generate a unique idempotency key once per logical operation, store it in a local variable, and include that exact key in every retry attempt. The backend must be configured to store the key and return the original response when the same key is received again.
import uuid; key = str(uuid.uuid4()) or js.crypto.randomUUID() via the FFI.Idempotency-Key) or as a JSON field in the request body.key value for every attempt.Because pyfetch is just a thin wrapper around the browser’s fetch, the same HTTP semantics apply. An abort or timeout on the client side does not guarantee that the server stopped processing; the write may still be committed.
import asyncio
import uuid
from pyodide.http import pyfetch
async def post_with_retry(url, payload, max_attempts=5):
idempotency_key = str(uuid.uuid4())
headers = {
"Content-Type": "application/json",
"Idempotency-Key": idempotency_key
}
backoff = 1.0
for attempt in range(1, max_attempts + 1):
try:
response = await pyfetch(url, method="POST", headers=headers, data=payload)
if response.status < 500:
return await response.json()
except Exception as e:
# Network‑level failure
pass
if attempt == max_attempts:
raise RuntimeError(f"Failed after {max_attempts} attempts")
await asyncio.sleep(backoff + (0.1 * attempt))
backoff *= 2
Duplicate writes typically occur when the idempotency key is regenerated inside the retry loop or omitted entirely, making each retry look like a new operation to the backend.
pyfetch delegates to the browser’s fetch.Idempotency-Key is the most common convention.Does your backend currently support idempotency key handling (e.g., a unique constraint on the key column or a cache that returns the original response)? If not, the client‑side pattern alone will not prevent duplicate writes.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.