ecache 2.2.0 copy "ecache: ^2.2.0" to clipboard
ecache: ^2.2.0 copied to clipboard

Provide a dart cache library usable on web, flutter and server side

Ecache #

A simple, flexible, and powerful caching library for Dart and Flutter, designed to be easy to use while providing a robust set of features for managing cached data.

Features #

  • Multiple Caching Strategies: Choose from several built-in eviction policies:
    • LRU (Least Recently Used): Evicts the least recently accessed items first.
    • LFU (Least Frequently Used): Evicts the least frequently accessed items first.
    • FIFO (First-In, First-Out): A simple strategy that evicts the oldest items first.
    • Expiration-based: Evicts items that have passed their expiration time.
  • Pluggable Architecture: The library is designed with a decoupled architecture, allowing you to mix and match components or create your own.
  • Asynchronous Value Production (Write-through cache): Automatically fetch and cache values that are expensive to compute or retrieve, ensuring the production logic runs only once for a given key.
  • Detailed Statistics: Monitor cache performance with built-in statistics tracking for hits, misses, and evictions.
  • Extensible Storage: While a simple Map-based storage is provided, you can create your own storage solutions (e.g., for disk-based or database-backed caching).
  • Null-Safe and Well-Documented: The entire API is null-safe and comes with comprehensive documentation.

Getting Started #

To use this library in your project, add it to your pubspec.yaml file:

dependencies:
  ecache: ^latest

Then, run pub get or flutter pub get.

Usage #

Creating a Simple Cache (FIFO) #

This is the most basic cache, which removes the oldest entry when the capacity is reached.

import 'package:ecache/ecache.dart';

void main() {
  // Create a cache with a capacity of 10
  final cache = SimpleCache<String, int>(capacity: 10);

  // Set and get values
  cache.set('a', 1);
  final value = cache.get('a'); // returns 1
  print('Value for key "a": $value');
}

Using a Least Recently Used (LRU) Cache #

This cache is ideal when you want to keep the most recently accessed items.

import 'package:ecache/ecache.dart';

void main() {
  final cache = LruCache<String, String>(capacity: 2);

  cache.set('user:1', 'Alice');
  cache.set('user:2', 'Bob');

  // Accessing 'user:1' makes it the most recently used
  cache.get('user:1');

  // Adding a new item will evict the least recently used ('user:2')
  cache.set('user:3', 'Charlie');

  print(cache.containsKey('user:2')); // false
}

Caching with an Expiration Time #

Set a default duration for all entries in the cache.

import 'package:ecache/ecache.dart';

void main() async {
  final cache = ExpirationCache<String, String>(
    capacity: 10,
    duration: const Duration(seconds: 5),
  );

  cache.set('session', 'active');
  print(cache.get('session')); // 'active'

  // Wait for the entry to expire
  await Future.delayed(const Duration(seconds: 6));

  print(cache.get('session')); // null
}

Eviction with Cleanup #

import 'package:ecache/ecache.dart';

void main() {
  final cache = SimpleCache(
    capacity: 20,
    onEvict: (key, value) {
      value.dispose(); // Clean up evicted items
    },
  );

  cache['key'] = 42;
  cache['halfKey'] = 21;
}

Get Statistics #

Enable statistics collection to monitor cache performance, detect memory leaks, and optimize cache configurations:

import 'package:ecache/ecache.dart';

void main() {
  // Enable statistics collection (only works in debug mode)
  StorageMgr().setEnabled(true);
  
  final userCache = SimpleCache<String, User>(capacity: 100);
  final sessionCache = SimpleCache<String, Session>(capacity: 50);

  // Perform some cache operations
  userCache.set('user1', User('Alice'));
  userCache.get('user1');     // hit
  userCache.get('user999');   // miss
  
  sessionCache.set('session1', Session('abc123'));
  
  // View individual cache statistics
  print(userCache.storage); // Shows hits, misses, evictions for this cache
  
  // View comprehensive report for all caches
  print(StorageMgr().createReport());
}

What you can accomplish with statistics:

  • Find Memory Leaks: Detect undisposed caches that continue to consume memory
  • Optimize Capacity: Analyze hit/miss ratios to determine if cache sizes are appropriate
  • Monitor Performance: Track cache effectiveness across your application
  • Debug Issues: Identify caches with unexpected eviction patterns
  • Resource Planning: Understand memory usage patterns for different cache types

Key Metrics Available:

  • hits/misses: Cache effectiveness ratio
  • currentEntries/maxEntries: Current vs peak memory usage
  • capacity: Configured maximum entries
  • evictions: How often items are removed due to capacity limits
  • sets: Total number of items stored

Example Report Output:

Storage Report (2024-01-15T10:30:45.123Z)
Storages registered: 2, unregistered: 0
  1, StatisticsStorage<String, User>: capacity: 100, maxEntries: 45, currentEntries: 42, hits: 156, misses: 23, evictions: 3, sets: 48
  2, StatisticsStorage<String, Session>: capacity: 50, maxEntries: 12, currentEntries: 12, hits: 89, misses: 5, evictions: 0, sets: 12

⚠️ Statistics collection only works in debug mode and has minimal performance impact.

Weak References #

import 'package:ecache/ecache.dart';

void main() {
  final storage = WeakReferenceStorage();
  final cache = SimpleCache(storage: storage, capacity: 20);
}

⚠️ Weak references allow garbage collection of older items beyond the guaranteed capacity.

  • Do not use eviction callbacks and weak storage together — callbacks may not fire when GC clears items.
  • WeakReferenceStorage are not able to produce statistics

Asynchronous Value Production (Write-through cache) #

Use getOrProduce to fetch and cache data from a database or a network API while making sure that multiple calls will fetch the data only once and all calls receive the same instance of the produced data.

import 'package:ecache/ecache.dart';

// A function that simulates fetching data from a network
Future<String> fetchUserData(String userId) async {
  print('Fetching data for $userId...');
  await Future.delayed(const Duration(seconds: 2)); // Simulate network latency
  return 'User data for $userId';
}

void main() async {
  final cache = SimpleCache<String, String>(capacity: 5);

  // The first call will trigger the fetchUserData function
  final data1 = await cache.getOrProduce('user:123', fetchUserData);
  print(data1);

  // The second call will return the cached data instantly
  final data2 = await cache.getOrProduce('user:123', fetchUserData);
  print(data2);
}

Performance #

Cache with 10.000 items stored already and performing 10.000 iterations of the methods below. Units are in microseconds.

At/near capacity (eviction pressure) #

Method Simple LRU LFU Expiration
get 0.22 0.29 0.62 0.39
set 2.11 0.27 0.84 0.25
containsKey 0.11 0.07 0.09 0.07
remove 1.31 0.23 0.62 0.47
getOrProduceSync 1.15 0.19 0.28 0.23

Plenty of free capacity #

Method Simple LRU LFU Expiration
get 0.05 0.06 0.13 0.10
set 0.11 0.15 0.16 0.20
containsKey 0.05 0.04 0.05 0.04
remove 0.09 0.14 0.18 0.21
getOrProduceSync 0.09 0.05 0.37 0.05

Architecture #

The library is built on three core components:

  • Cache: The main interface that developers interact with. Concrete implementations like SimpleCache, LruCache, and LfuCache provide the caching logic.
  • Storage: An abstraction for the underlying key-value store. The default is SimpleStorage, which uses a LinkedHashMap, but custom implementations can be created.
  • Strategy: The logic that governs how and when entries are evicted from the cache. Each cache type uses a corresponding strategy (e.g., LruStrategy).

This decoupled design allows for great flexibility in composing caches that fit specific needs.

Contributing #

Contributions are welcome! Please feel free to open an issue or submit a pull request.

License #

This library is licensed under the MIT License. See the LICENSE file for details.

2
likes
150
points
295
downloads

Publisher

unverified uploader

Weekly Downloads

Provide a dart cache library usable on web, flutter and server side

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

More

Packages that depend on ecache