Limits of Backbone.sync retry behavior for model writes
0 reputation · 01 Aug 2020, 22:47 UTC
0 reputation · 01 Aug 2020, 22:47 UTC
The goal is to decide whether Backbone.sync should expose a configurable retry mechanism for failed model.save requests while guaranteeing that retries do not create duplicate writes. The default sync implementation delegates to jQuery.ajax and contains no retry logic, so any retry behavior must be added in application code or via a custom sync override. Developers must ensure that retries are safe for non‑idempotent operations such as POST creates, and that a global sync override does not unintentionally affect unrelated models or collections.
Should Backbone.sync provide a built‑in retry option? If so, what default number of attempts and back‑off strategy would be appropriate? How can the library help developers enforce idempotency when retries are enabled?
No — Backbone.sync should not gain a built-in, default-on retry mechanism, and it does not have one today. The default sync simply delegates to Backbone.ajax (which wraps jQuery.ajax by default), and one model.save() call produces exactly one HTTP request. Any retry behavior you see in an app comes from somewhere else — a jQuery transport, a service worker, a proxy, or an application-level wrapper — not from Backbone.
sync picks the HTTP verb from model.isNew(): a model without an id is saved with POST to the collection URL, while a model with an id is saved with PUT (or PATCH with {patch: true}) to the model URL. Blindly retrying a POST create after a network failure can duplicate the write: the first request may have succeeded server-side even though the response never arrived. Also note that save() does not deduplicate concurrent calls — two saves on a new model already produce two POSTs, so adding automatic retries on top would multiply that risk.
A reasonable opt-in policy (off by default):
Retry-After. Never auto-retry 400/401/403/422 — they will fail identically.options.success/options.error exactly once, after the final attempt (the request event may fire per attempt), and serialize the payload once before the first attempt so retries don't pick up attribute changes made while a retry is pending.The library (or your wrapper) can make retries safe in two ways:
id before the first save(). This converts the create into an idempotent PUT to a deterministic URL like /collection/<id>. Caveat: this only works if your API accepts client-supplied identifiers; it breaks against servers that assign auto-increment ids.save() call and resend it as a header on every attempt, so the server can deduplicate. A retry wrapper should skip retries for POST unless one of these mechanisms is present.Treat PATCH like POST unless verified: it is only idempotent for set-to-value semantics, not for handlers that increment or append.
Prefer overriding Backbone.ajax or defining a sync function on individual model/collection classes rather than replacing Backbone.sync globally. A global override changes fetch, save, and destroy for every model and collection in the app and can silently alter unrelated code paths.
Backbone.sync and Backbone.ajax in your installed node_modules/backbone/backbone.js to confirm there is no retry option and to see how the ajax call is made — internals vary by version, so check against your lockfile.Backbone.ajax to log calls; save a new model and a persisted model and confirm one POST and one PUT, with no second call after a failure./collection/<id> when a client id is set.One caveat: Backbone is effectively in maintenance mode, so expect an adapter or plugin rather than an upstream core change. The deciding detail for your design is server capability — whether it accepts client ids or honors an idempotency-key header determines which mechanism to use.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.