Struct fastly::cache::core::Transaction

source ·
pub struct Transaction { /* private fields */ }
Expand description

A cache transaction initiated by Transaction::lookup().

Transactions coordinate between concurrent actions on the same cache key, incorporating concepts of request collapsing and revalidation, though at a lower level that does not automatically interpret HTTP semantics.

§Request collapsing

If there are multiple concurrent calls to Transaction::lookup() for the same item and that item is not present, just one of the callers will be instructed to insert the item into the cache as part of the transaction. The other callers will block until the metadata for the item has been inserted, and can then begin streaming its contents out of the cache at the same time that the inserting caller streams them into the cache.

§Revalidation

Similarly, if an item is usable but stale, and multiple callers attempt a Transaction::lookup() concurrently, they will all be given access to the stale item, but only one will be designated to perform an asynchronous update (or insertion) to freshen the item in the cache.

§Example

Users of the transactional API should at minimum anticipate lookups that are obligated to insert an item into the cache, and lookups which are not. If the stale-while-revalidate parameter is set for cached items, the user should also distinguish between the insertion and revalidation cases.

const TTL: Duration = Duration::from_secs(3600);
// perform the lookup
let lookup_tx = Transaction::lookup(CacheKey::from_static(b"my_key"))
    .execute()
    .unwrap();
if let Some(found) = lookup_tx.found() {
    // a cached item was found; we use it now even though it might be stale,
    // and we'll revalidate it below
    use_found_item(&found);
}
// now we need to handle the "must insert" and "must update" cases
if lookup_tx.must_insert_or_update() {
    if let Some(stale_found) = lookup_tx.found() {
        // a cached item was found and used above, and now we need to perform
        // revalidation
        let revalidation_contents = build_contents();
        if should_replace(&stale_found, &revalidation_contents) {
            // use `insert` to replace the previous object
            let mut writer = lookup_tx
                .insert(TTL)
                .surrogate_keys(["my_key"])
                .known_length(revalidation_contents.len() as u64)
                .execute()
                .unwrap();
            writer.write_all(revalidation_contents).unwrap();
            writer.finish().unwrap();
        } else {
            // otherwise update the stale object's metadata
            lookup_tx
                .update(TTL)
                .surrogate_keys(["my_key"])
                .execute()
                .unwrap();
        }
    } else {
        // a cached item was not found, and we've been chosen to insert it
        let contents = build_contents();
        let (mut writer, found) = lookup_tx
            .insert(TTL)
            .surrogate_keys(["my_key"])
            .known_length(contents.len() as u64)
            // stream back the object so we can use it after inserting
            .execute_and_stream_back()
            .unwrap();
        writer.write_all(contents).unwrap();
        writer.finish().unwrap();
        // now we can use the item we just inserted
        use_found_item(&found);
    }
}

Implementations§

source§

impl Transaction

source

pub fn lookup(key: CacheKey) -> TransactionLookupBuilder

Returns a TransactionLookupBuilder that will perform a transactional cache lookup.

See Transaction for details and an example.

source

pub fn found(&self) -> Option<Found>

Returns a Found object for this cache item, if one is available.

Even if an object is found, the cache item might be stale and require updating. Use Transaction::must_insert_or_update() to determine whether this transaction client is expected to update the cached item.

source

pub fn must_insert(&self) -> bool

Returns true if a usable cached item was not found, and this transaction client is expected to insert one.

Use Transaction::insert() to insert the cache item, or Transaction::cancel_insert_or_update() to exit the transaction without providing an item.

source

pub fn must_insert_or_update(&self) -> bool

Returns true if a fresh cache item was not found, and this transaction client is expected to insert a new item or update a stale item.

Use:

source

pub fn cancel_insert_or_update(&self) -> Result<(), CacheError>

Cancels the obligation for this transaction client to insert or update a cache item.

If there are concurrent transactional lookups that were blocked waiting on this client to provide the item, one of them will be chosen to be unblocked and given the Transaction::must_insert_or_update() obligation.

This method should only be called when Transaction::must_insert_or_update() is true; otherwise, a CacheError::InvalidOperation will be returned.

source

pub fn insert(self, ttl: Duration) -> TransactionInsertBuilder

Returns a TransactionInsertBuilder that will perform a transactional cache insertion.

This method should only be called when Transaction::must_insert_or_update() is true; otherwise, a CacheError::InvalidOperation will be returned when attempting to execute the insertion.

source

pub fn update(self, ttl: Duration) -> TransactionUpdateBuilder

Returns a TransactionUpdateBuilder that will perform a transactional cache update.

Updating an item freshens it by updating its metadata, e.g. its age, without changing the object itself.

This method should only be called when Transaction::must_insert_or_update() is true and the item is found (i.e. Transaction::found() is non-empty). Otherwise, a CacheError::InvalidOperation will be returned when attempting to execute the update.

The method consumes the transaction. Call Transaction::found() before this method if subsequent access to the stale cached item is needed.

Important note: the TransactionUpdateBuilder will replace all of the configuration in the underlying cache item; if any configuration is not set on the builder, it will revert to the default value. So, for example, if a cached item previously had some surrogate keys set, and you want to retain them, you must call TransactionUpdateBuilder::surrogate_keys() with the desired keys. Most configuration is available in the Found object.

Note: the above behavior is likely to be replaced with defaulting the builder to the existing configuration, making it easier to retain the configuration by default. This change will be noted in a future changelog.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.