Docs / ArchitectureKit / Guides / Caching States

Caching States

If no event carries the whole state, so that the state can not start from the latest event of one type (see Reading Long Streams), the store can keep the states of the most recently used subjects in memory instead, so that the next command on one of them reads only the events written since. To do so, hand over the WithStateCache option with the number of subjects to keep when creating the store:

store := architecturekit.NewStore(client, "https://library.eventsourcingdb.io", architecturekit.WithStateCache(10_000))

Once the cache is full, the least recently used subject makes room. Such a subject is read in full again the next time, or from the latest event of the type given to FromLatest, if there is one (see Reading Long Streams).

The cache only holds what was read, never what a command has written. Every command reads all events after the ones its state was built from, including those written by other processes, so the cache stays correct if several processes write to the same subjects.

A cached state is handed to several commands, possibly at the same time. That is safe for a state that consists of values only, such as the Book state (see Defining State). A state that holds slices, maps or pointers is only cached if it has a Clone function, which returns a copy that shares no data with the original:

type Shelf struct {
  BookIDs []string
}

var shelfState = architecturekit.NewState(Shelf{}).
  Evolve(func(shelf Shelf, event BookShelved) Shelf {
    shelf.BookIDs = append(shelf.BookIDs, event.BookID)
    return shelf
  }).
  Clone(func(shelf Shelf) Shelf {
    return Shelf{BookIDs: slices.Clone(shelf.BookIDs)}
  })

Without a Clone function, such a state is read as without a cache.

Note that a time.Time counts as a value, since its location never changes.

Note that values below 1 count as 1, and that calling Clone twice panics.