ConcurrentCaller wasn't waiting properly if start() was called again
while it was pausing, so 429 caused an immediate retry, which is pretty
much exactly what you don't want a 429 to do.
- Move 5xx retries and connection checking out of the sync API client
and into HTTP.request() so that they apply to all requests. 429 handling
remains in the API client, since not all callers necessarily want to
handle that the same way. Callers can still handle 5xx themselves by
including the relevant 5xx status codes in `successCodes` or by passing
`errorDelayMax: 0`.
- Add `cancellerReceiver` option, which is a callback that receives a
function that will cancel the request, whether it's an active request
or an automatic delay before a 5xx retry.
This also updates Sinon to 7.3.2.
Previously, objects were first downloaded and saved to the sync cache,
which was then processed separately to create/update local objects. This
meant that a server bug could result in invalid data in the sync cache
that would never be processed. Now, objects are saved as they're
downloaded and only added to the sync cache after being successfully
saved. The keys of objects that fail are added to a queue, and those
objects are refetched and retried on a backoff schedule or when a new
client version is installed (in case of a client bug or a client with
outdated data model support).
An alternative would be to save to the sync cache first and evict
objects that fail and add them to the queue, but that requires more
complicated logic, and it probably makes more sense just to buffer a few
downloads ahead so that processing is never waiting for downloads to
finish.
Closes#864
This adds a 'channel' property to Zotero.HTTP.UnexpectedStatusException,
because the 'channel' property of the XHR can be garbage-collected
before handling, and the channel's 'securityInfo' property is necessary
to detect certificate errors.