Skip to content

Zeptosec/event-cache

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

57 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Tests codecov JSR NPM Version Publish License: MIT GitHub last commit (branch)

Event Cache

The main purpose of this libary is to store key-value pairs while also managing the expiration of those entries and emitting events when items expire. This is useful for scenarios where you need a cache that automatically removes stale data and notifies other parts of your application when that happens.

Library description

Provided functions

A table documenting all public methods of each class

EventCache class

Method Name Parameters Description
constructor options: EventCacheProps<K> - deleteOnExpire?: boolean: Whether to delete expired items (defaults to true). - eventStrategy: EventStrategy<K>: The event expiration strategy. Initializes a new EventCache instance, setting the deleteOnExpire option and the eventStrategy.
set key: K value: V. key: The key to add/update in the cache. value: The value to store associated with the key. Adds or updates a key-value pair in the cache. It also manages the expiration event for the item. Returns this (the EventCache instance).
delete key: K. The key to remove from the cache. Removes a key-value pair from the cache and the associated expiration event. Returns true if an item was removed, false otherwise.
get key: K. The key to retrieve from the cache. Retrieves the value associated with the given key. Returns the value if found, undefined otherwise.
clear None Clears all key-value pairs from the cache and removes all expiration events.
size None A getter that returns the number of items currently in the cache.
entries Returns an iterator of [key, value] pairs for each entry in the cache Returns an iterator of [key, value] pairs for each entry in the cache.
keys None Returns an iterator of keys for each entry in the cache.
values None Returns an iterator of values for each entry in the cache.
has key: K. The key to check for. Checks if the cache contains a key. Returns true if the key exists, false otherwise.
forEach callback: (value: V, key: K, map: Map<K, V>) => void A function to execute for each key-value pair. It receives the value, key, and the map as arguments. Iterates over each key-value pair in the cache and executes the provided callback function.
addEventListener type: T extends keyof EventCacheEvents<K, V> listener: (event: EventCacheEvents<K, V>[T]) => void options?: boolean A function that will be called when an item in a cache expires.

Business problems that library solves

This library helps to solve several business problems related to caching and event management:

  • Managing expired data: Many applications need to cache data for permormance, but that data often has a limited lifespan. This library automates removal of old data while still working as a cache.
  • Event based notifications: Provides a convenient way of knowing when data becomes outdated through events.
  • Flexible expiration strategies: For better performance there's interval based strategy which can handle many more events. For more accuracy there's timeout based strategy.

Intended use

This library is intended for use in applications that requires caching with data expiration.

  • Real-time data: frequently updated data, such as price discounts, weather information. Once data expires it can easily be updated again when event is received.
  • Rate-limiting: Caching responses from APIs with rate limits. It can cache frequently fetched responses so that the database isn't used on every request.
  • Web applications: Caching frequently used data to improve performance and user expirience. Caching API responses on the front end.

This library is useful in any situation where data needs to be cached for performance while expired data is removed.

Restrictions for library usage

Here are outlined the main restrictions for using the library

Technical restrictions

  • Event Listener Management: addEventListener provides a way to attach an event to an object to receive events after the data expired. This event should be removed with AbortController after it is no longer needed.
  • Implementation limitations: EventCache triggers events asynchronously. This means that expiration events are not guaranteed to happen exactly at specified time. Slight delays should be expected.

Business restrictions

  • Expiry requirement: This libary is primarily for caching data that will expire. If your data never expires then a simpler caching solutions might be more appropriate.

  • Data sensitivy: This library doesn't provide any data encryption. It is stored as provided.

Other important information

Performance

The performance of event cache is directly related to EventStrategy. So it is very important to choose event strategy that you need. IntervalStrategy is more performant, but events may not be received on time. TimeoutStrategy should be more accurate than IntervalStrategy, but it may consume more memory when there's a lot of data.

Features

  • Time-based expiration (TimeoutStrategy): Items expire after a specified time-to-live (TTL).
  • Interval-based expiration (IntervalStrategy): Items are checked for expiration at regular intervals.
  • Event-driven expiration: Events are emitted when items expire, allowing you to perform cleanup or other actions.
  • Configurable deleteOnExpire: Choose whether to automatically remove expired items from the cache or retain them.
  • Extensible: Easily create custom expiration strategies.
  • Simple API: Easy to use and integrate into your projects.

Class diagram

Below is the class diagram illustrating the architecture of the Event Cache library.

Class diagram

Examples

The EventCacheFactory provides convenient methods to create caches with predefined strategies:

import { EventCacheFactory } from "@zeptosec/event-cache";

// Time-based expiration using TimeoutStrategy
const timeoutCache = EventCacheFactory.createEventCacheTimeout<string, number>(
  5000,
); // Items expire after 5 seconds

// Create AbortController for removing event listener if needed
const abortController = new AbortController();

timeoutCache.addEventListener("expire", (expiredItem) => {
  console.log(`Item '${expiredItem.key}' expired. Value: ${expiredItem.value}`);
}, {
  signal: abortController.signal,
});

timeoutCache.set("myKey", 123);

setTimeout(() => {
  console.log(`myKey (3000 ms): ${timeoutCache.get("myKey")}`);
  // Access the value before expiration
}, 3000);

setTimeout(() => {
  console.log(`myKey (7000 ms): ${timeoutCache.get("myKey")}`);
  // Access the value after expiration

  abortController.abort(); // Remove event listener
}, 7000);

// Interval-based expiration using IntervalStrategy
const intervalCache = EventCacheFactory.createEventCacheInterval<
  string,
  string
>(2000); // Items are checked every 2 seconds

intervalCache.addEventListener("expire", (expiredItem) => {
  console.log(`Item '${expiredItem.key}' expired. Value: ${expiredItem.value}`);
});

intervalCache.set("key2", "hello");

setTimeout(() => {
  console.log(`key2 (1000 ms): ${intervalCache.get("anotherKey")}`);
  // Access the value before expiration
}, 1000);

setTimeout(() => {
  console.log(`key2 (3000 ms): ${intervalCache.get("anotherKey")}`);
  // Access the value after expiration
}, 3000);

Using EventCacheBuilder to build your own EventCache:

import { EventCacheBuilder, IntervalStrategy } from "@zeptosec/event-cache";

const builder = new EventCacheBuilder<number, string>();
// Items are checked every 500ms, TTL is 1000ms, and expired items are NOT deleted.
const intervalCacheCustom = builder
  .withStrategy(
    new IntervalStrategy(1000, 500),
    false,
  ).build();

intervalCacheCustom.addEventListener("expire", (expiredItem) => {
  console.log(`Item '${expiredItem.key}' expired. Value: ${expiredItem.value}`);
});

intervalCacheCustom.set(1, "test");

setTimeout(() => {
  console.log(`1 (2000ms): ${intervalCacheCustom.get(1)}`); // Value should still be present even after expiration.
}, 2000);

setTimeout(() => {
  console.log(`1 (3000ms): ${intervalCacheCustom.get(1)}`); // Value should still be present even after expiration.
}, 3000);

Using EventCache with strategy directly:

import { EventCache, TimeoutStrategy } from "@zeptosec/event-cache";

const eventCache = new EventCache<number, string>({
  deleteOnExpire: false, // do not delete after expired
  eventStrategy: new TimeoutStrategy(1000), // TTL: 1 second
});

eventCache.set(42, "answer");

eventCache.addEventListener("expire", ({ key, value }) => {
  console.log(`expired: ${key}: ${value}`);
});

setTimeout(() => {
  console.log(`Before expired: ${eventCache.get(42)}`);
  // value is defined
}, 500);

setTimeout(() => {
  console.log(`After expired: ${eventCache.get(42)}`);
  // value is still defined
}, 2000);

Analysis of alternative packages

There are many different packages for caching data and receiving event when it expires.

@cacheable has more features and is very popular.

@cache-manager Similar to @cacheable has a lot of different functionality for managing the cache like emitting event when key is set or deleted.

@node-cache Also very popular package with the similar functionaly and many more features.

@buffered-queue stores a list of items and then after specified time returns an event with all the data in the queue or if the size limit is reached.

So there are many other packages to choose from if you find that this pacakage is lacking some features that you need.

About

An in-memory cache with event-driven expiration.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published