Support for durable mutations that can be automatically retried against a remote until a positive confirmation of success is received, or a specific kind of error indicates it cannot succeed.
You must configure your application in order to use this support. In particular, a durable storage for the mutations must be defined so that they can survive application restarts, power failures, etc. Your implementation of that storage will determine the overall reliability of these mutations. Additionally, you may define a custom optimistic tempid strategy that can do tempid remappings before ever talking to the server.
The mutations in a durable transaction must be:
This system asserts that the mutation in question will eventually run. However, it exposes the details of what is really going on at the mutation layer so that you can customize the behavior as needed.
IMPORTANT: The optimistic side of the mutation will run any time the mutation is tried. You can check (via
durable-mutations/retry?
) if this is a retry, and decide if there is anything to do in the UI.
Optimistic temporary ID remapping is supported, but requires that you define a TempidStrategy. This can be done by either pre-allocating a range from the server, or by allowing the client to use generated UUIDs (i.e. the UUID within the tempid itself). Your server must agree on this strategy for consistent operation.
It is a valid strategy to not resolve the tempids on the client, in which case your app state will have tempids on the items submitted until the real mutation completes. Tempid rewrites from the server are always applied, even if you had previously remapped them on the client.
Server-side implementations of durable mutations cannot have any meaningful return value other than :tempids
since
there is no guarantee when the real mutation will run.
There is a closure goog.define of BACKOFF_LIMIT_MS which defaults to 30000 (ms). This limits the maximum wait time between mutation retries and can be modified via compiler settings. See compiler docs.
NOTE: The backoff will cause mutations to appear somewhat slowly when network communication resumes. This is by design, and will prevent a flood of requests after a server outage. Caution: Setting this too low can cause blockage on retry logic when the network is down.
There is also a LOOP_TIMEOUT_MS (default 20ms). Any durable mutation transact submission, on average, appear to have this much latency. Setting it too low will cause the browser to waste a lot of CPU looking for trouble, setting it too high will annoy the user. DO NOT SET THIS TO 0.
Installation: See with-durable-mutations
.
Usage: Use transact!
from this ns, or add :com.fulcrologic.fulcro.offline.durable-mutations/durable? true
to the
options of your call to comp/transact!
. Do not include follow-on reads in the transaction if using comp/transact!
.
Support for durable mutations that can be automatically retried against a remote until a positive confirmation of success is received, or a specific kind of error indicates it cannot succeed. You must configure your application in order to use this support. In particular, a durable storage for the mutations must be defined so that they can survive application restarts, power failures, etc. Your implementation of that storage will determine the overall reliability of these mutations. Additionally, you *may* define a custom optimistic tempid strategy that can do tempid remappings before ever talking to the server. The mutations in a durable transaction *must* be: * Idempotent * Order-independent This system asserts that the mutation in question *will eventually* run. However, it exposes the details of what is really going on at the mutation layer so that you can customize the behavior as needed. IMPORTANT: The optimistic side of the mutation will run any time the mutation is tried. You can check (via `durable-mutations/retry?`) if this is a retry, and decide if there is anything to do in the UI. Optimistic temporary ID remapping is supported, but requires that you define a TempidStrategy. This can be done by either pre-allocating a range from the server, or by allowing the client to use generated UUIDs (i.e. the UUID within the tempid itself). *Your server must agree on this strategy for consistent operation*. It is a valid strategy to *not* resolve the tempids on the client, in which case your app state will have tempids on the items submitted until the real mutation completes. Tempid rewrites from the server are always applied, even if you had previously remapped them on the client. Server-side implementations of durable mutations cannot have any meaningful return value other than `:tempids` since there is no guarantee when the real mutation will run. There is a closure goog.define of BACKOFF_LIMIT_MS which defaults to 30000 (ms). This limits the maximum wait time between mutation retries and can be modified via compiler settings. See compiler docs. NOTE: The backoff will cause mutations to appear somewhat slowly when network communication resumes. This is by design, and will prevent a flood of requests after a server outage. Caution: Setting this too low can cause blockage on retry logic when the network is down. There is also a LOOP_TIMEOUT_MS (default 20ms). Any durable mutation transact submission, on average, appear to have this much latency. Setting it too low will cause the browser to waste a lot of CPU looking for trouble, setting it too high will annoy the user. DO NOT SET THIS TO 0. Installation: See `with-durable-mutations`. Usage: Use `transact!` from this ns, or add `:com.fulcrologic.fulcro.offline.durable-mutations/durable? true` to the options of your call to `comp/transact!`. Do not include follow-on reads in the transaction if using `comp/transact!`.
(attempt mutation-env)
Returns the attempt number of the mutation (using that mutation's env).
Returns the attempt number of the mutation (using that mutation's env).
(cancel-mutation!
{:com.fulcrologic.fulcro.algorithms.tx-processing/keys [options] :keys [app]})
Can be called from your mutation error-action
(or result-action
) to cancel any future attempts of the
current instance of the current mutation.
Can be called from your mutation `error-action` (or `result-action`) to cancel any future attempts of the current instance of the current mutation.
(current-mutation-store app)
Returns the current mutation store of the given Fulcro app
Returns the current mutation store of the given Fulcro app
(current-tempid-strategy app)
Returns the current tempid strategy of the given Fulcro app
Returns the current tempid strategy of the given Fulcro app
(is-retry? mutation-env)
Returns true if the mutation env represents an environment where the mutation is being retried. Returns false only on the initial submission of the mutation
Returns true if the mutation env represents an environment where the mutation is being retried. Returns false only on the initial submission of the mutation
(transact! app-ish txn)
(transact! app-ish txn options)
Similar to comp/transact!, but the mutations in the transaction will first be written to a persistent store (on mutation per entry), and then each will be retried until they succeed or are explicitly cancelled. Transactions submitted with this function must be safe to run in parallel in any order, and any number of times (the semantic is that they will be retried until they succeed at least once).
WARNING: YOU MUST ENSURE ORDER DOES NOT MATTER ON A PER-MUTATION BASIS (order cannot be preserved
without risking deadlock). This means [(f) (g) (h)]
might run on the server as, for example,
[(g)]
, two hours pass [(f)]
, 5 seconds pass [(h)]
.
Success is defined (by default) by your application's remote-error?
. When there is no detected error the mutation
will be removed from durable storage and your mutation's ok-action
will be called. The mutation's error-action
section
can also decide that the attempt was "good enough" and explicitly cancel further retries with dm/cancel-mutation!
.
Your mutation's action
and result-action
(or if you use them: error-action
or ok-action
) will be called
on every attempt.
Your mutation's action
body can use (dm/retry? env)
to determine if this is the first attempt, and (dm/attempt env)
to find out how many times it has been tried.
The result-action
(or error-action
) sections of your mutation can call (dm/cancel-mutation! env)
to remove
the durable mutation and stop retrying it. For example, you might decide that it is an application error that should
be reported (e.g. for support purposes) via a different (possibly durable) mutation.
If the remote succeeds (remote-error?
of your app reports false
), then the mutation will automatically be
removed from the persistent storage and your ok-action
will be triggered.
The durability of these mutation across application restarts is dependent upon the implementation of the EDN store used with this facility.
NOTES:
transact!
are essentially considered to be legal to run or repeat at any time.
This also means that the optimistic actions of the transaction are not guaranteed to run in any total order
compared to any other transactions in Fulcro.WARNING: This function is exactly equivalent to comp/transact!
unless you install support for it on your application.
See with-durable-mutations
.
Similar to comp/transact!, but the mutations in the transaction will first be written to a persistent store (on mutation per entry), and then each will be retried until they succeed or are explicitly cancelled. Transactions submitted with this function must be safe to run in parallel in any order, and any number of times (the semantic is that they will be retried until they succeed *at least once*). WARNING: YOU MUST ENSURE ORDER DOES NOT MATTER ON A PER-*MUTATION* BASIS (order cannot be preserved without risking deadlock). This means `[(f) (g) (h)]` might run on the server as, for example, `[(g)]`, two hours pass `[(f)]`, 5 seconds pass `[(h)]`. Success is defined (by default) by your application's `remote-error?`. When there is no detected error the mutation will be removed from durable storage and your mutation's `ok-action` will be called. The mutation's `error-action` section can also decide that the attempt was "good enough" and explicitly cancel further retries with `dm/cancel-mutation!`. Your mutation's `action` and `result-action` (or if you use them: `error-action` or `ok-action`) will be called *on every attempt*. Your mutation's `action` body can use `(dm/retry? env)` to determine if this is the first attempt, and `(dm/attempt env)` to find out how many times it has been tried. The `result-action` (or `error-action`) sections of your mutation can call `(dm/cancel-mutation! env)` to remove the durable mutation and stop retrying it. For example, you might decide that it is an application error that should be reported (e.g. for support purposes) via a different (possibly durable) mutation. If the remote succeeds (`remote-error?` of your app reports `false`), then the mutation will automatically be removed from the persistent storage and your `ok-action` will be triggered. The durability of these mutation across application restarts is dependent upon the implementation of the EDN store used with this facility. NOTES: * The optimistic actions of the mutations in the transaction will fire *every time the mutation is tried*. * Operations submitted via this `transact!` are essentially considered to be legal to run or repeat at any time. This also means that the optimistic actions of the transaction are *not* guaranteed to run in any total order compared to any other transactions in Fulcro. * If you submit a transaction with more than one mutation it will actually be split into multiple transaction submissions (one mutation per transaction). This is to ensure that retries happen on a mutation granularity, since we cannot enforce full-stack transactional semantics. WARNING: This function *is exactly equivalent to* `comp/transact!` unless you install support for it on your application. See `with-durable-mutations`.
(with-durable-mutations app mutation-store tempid-strategy)
Augments the given app with support for write-through mutations. RETURNS A NEW APP. You must use this like so:
(defonce app (-> (fulcro-app ...)
(with-durable-mutations mutation-store tempid-strategy)))
mutation-store
is an implementation of MutationStore.tempid-strategy
is an implementation of TempIdStrategyAugments the given app with support for write-through mutations. RETURNS A NEW APP. You must use this like so: ``` (defonce app (-> (fulcro-app ...) (with-durable-mutations mutation-store tempid-strategy))) ``` * `mutation-store` is an implementation of MutationStore. * `tempid-strategy` is an implementation of TempIdStrategy
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close