xAPI transport¶
Getting a statement to an LRS over a network that is not always there. This is the part of the xAPI support with real behaviour in it, and the part worth reading before you trust a delivery guarantee.
| File | Job |
|---|---|
IXApiTransport | The send seam — one method, so tests can fake a network |
UnityWebRequestXApiTransport | The real sender |
QueuedXApiTransport | FIFO queue, retry with backoff, persistence |
XApiDeliveryResult | The tri-state outcome the queue decides on |
XApiEndpointConfig | Endpoint, auth, and the activity id statements hang off |
XApiQueueOptions | Attempt count, backoff bounds, queue cap, persistence key |
The three outcomes¶
XApiDeliveryResult is Delivered, Retryable, or Rejected, and the whole queue turns on telling them apart:
- Delivered — drop it, it is recorded.
- Retryable — keep it and try again later. Timeouts, 5xx, no connection.
- Rejected — drop it, because retrying cannot help. A malformed statement is still malformed in ten minutes, and a queue that retries it forever stops delivering everything behind it.
A 409 counts as delivered. The LRS is saying "I already have this statement id", which is the correct answer to a retry that succeeded the first time and lost the response.
The enum is internal. Callers get Task<bool> — see the xAPI overview for why.
Authentication failures halt without dropping¶
401, 403 and 407 are not rejections, even though they are 4xx. A wrong or expired credential is a configuration problem, not a bad statement: the statements in the queue are fine and will deliver as soon as the credential is. So the queue stops sending, keeps everything, and logs it — rather than discarding a session's worth of learner data because a token expired mid-course.
Persistence¶
With Persist on, the queue survives the session: PlayerPrefs, which is IndexedDB on WebGL. A failed persist is logged as an error, not a warning, because the queue will keep running and look healthy while nothing it holds would survive a reload.
BindToLearner scopes the stored queue to a learner. It only adopts a backlog that has no learner scope yet — statements belonging to a different learner are left alone rather than inherited, which is the behaviour you want on a shared machine.
Options worth setting deliberately¶
MaxQueuedStatements defaults to 256 and MaxAttempts to 3, with backoff from 500ms to 8s. The cap exists because an unbounded queue on a long offline session is a memory leak with a learner's progress in it; when it is reached the oldest statements go first, on the grounds that the recent ones describe where the learner actually is.