Concepts

Carts

This documentation refers to the system to keep Carts locally, refreshing them regularly and returning them on fetch requests if the data is considered up to date.

Improvements to consider

  • Move everything we can do run to the last shutdown action after the response, to improve response times.
  • During the cart refresh cron job, could we send all of the cart refresh requests at once?

How admira handles carts

In admira, carts are kept alive for 5 minutes. If POST carts/{uuid}/refresh is not made within this time period, the cart is expired and no other operations are allowed.

Table

Carts are kept in a dedicated table, which contains the following:

Table name: admira_carts

ColumnTypeDefault valueDescription
cart_idvarchar(255)admira cart identifier
session_idvarchar(256)Identifier for the session that created and holds the cart
statusvarchar(255)OUTDATEDStatus (local cart status, not admira) (UP-TO-DATE, OUTDATED, EXPIRED)
date_createdTEXT0000-00-00T00:00:00+00:00Creation date
date_updatedTEXT0000-00-00T00:00:00+00:00Last update date (used to keep track of refreshing times specifically)
dataTEXT{}Cart response body

Cart creation

When a cart is created, we catch the response to it, fetch the UUID from the Location header and create it in the database. This new cart is just its UUID and dates (which are the current date upon creation). The rest is set to the default values. The next time there is a fetch, this local entry is ignored and the cart is fetched from admira and the data is updated in the database.

Cart Fetching

When a cart is fetched, before the request is made, we check the local entry. If there is a cart, we verify the following:

  1. The cart is up to date.
  2. Its creation date is before the time limit (see settings).
  3. Its updated date (refresh) is before the 5 minute keep alive window for admira.

If they are all true, we return the cart data in the database. If only 1. is false, we let the normal proxied API request go through to admira. Otherwise we return a 404 response, without consulting admira.

When we fetch from admira, we update the local cart setting the state and contents appropriatly:

  • If the cart still exists, we set the status to UP-TO-DATE and update the data.
  • If the cart returns 404, we set the status to EXPIRED.

Cart Refreshing

Refreshing carts is done via a cron job. This cron job needs to run at a recurrence smaller than the cart refresh window and with enough time gap to be able to actually refresh in time.

Similarly, the window of time after which a cart should be deemed "refreshable" should be chosen correctly in order to minimize the amount of requests to admira but also to maximize the available time to do the refresh calls.

Another thing to be considered is the recurrence of cron hits to WordPress to check if there is any job to run. Most only allow for 1 minute at the minimum.

The cron job will gather all non expired carts deemed to be refreshable. The ones that are past their refresh window or past their max alive window, will be set as EXPIRED. For the rest of the carts there will be a request to the admira API to refresh them.

Upon successful refresh, their updated date will be set to the current date.

Cart Invalidation

admira routes that update/modify the cart's contents will invalidate (status set to OUTDATED) the local cart when the response is OK 2XX.

Cart Locks

When there are multiple requests to admira to modify the same cart, it can decide to reject them, regardless of the order they came in.

Even if we assume the frontend to our proxy API does not send concurrent requests, they might collide with our internal refreshing requests, leading to possible failures.

In order to combat this, we implemented a locking system using MySQL's locking functions. These locking functions allow us to creat locks around a string key. These locks can be obtained by one MySQL session (equivalent to one WordPress PHP session) and cannot be obtained by other sessions until the lock is released. These locks have a timeout associated with it when trying to get the lock. After that timeout, the query returns without having obtained the lock. The timeout seconds can be set by the constant ADMIRA_PROXY_LOCK_TIMEOUT. By default, timeout is 5 seconds.

A request to our proxy API creates a lock for a particular cart UUID, and the next request will have to wait until this one finishes or it timeouts. When the request finishes, it releases the lock it acquired. This locking obtain/release is done via a Guzzle middleware to get as close to the requests as possible in order to minimize waiting time for other requests.

Only POST requests that modify the cart are considered for locking. We use the same system for Cart Invalidation to determine which ones.

A timeout of 5 seconds is considered enough to handle almost all cases since most requests take much less than that and it is not expected that there will be enough concurrent requests to reach this in one of them.

When the lock releases, the next request to obtain it is chosen at random.

Cart Cleanup

Expired carts are removed from the database after a defined amount of time (see settings on how to change this) via a cron job. Carts can be kept long enough in the database to allow for debugging with logs.

Previous
Settings