ecache 2.1.2
ecache: ^2.1.2 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 ratiocurrentEntries/maxEntries
: Current vs peak memory usagecapacity
: Configured maximum entriesevictions
: How often items are removed due to capacity limitssets
: 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);
}
Architecture #
The library is built on three core components:
Cache
: The main interface that developers interact with. Concrete implementations likeSimpleCache
,LruCache
, andLfuCache
provide the caching logic.Storage
: An abstraction for the underlying key-value store. The default isSimpleStorage
, which uses aLinkedHashMap
, 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.