get<T> method
Retrieves the value associated with the given key
.
Returns null
if no value is found for the given key
or if the value is expired.
If refreshCallback
is provided and the item is stale according to its policy,
the callback will be used to refresh the data based on the refresh strategy.
Throws an ArgumentError if the key is empty. Throws a CacheException if there is an error retrieving the data.
Implementation
Future<T?> get<T>(String key,
{Future<T> Function()? refreshCallback, CachePolicy? policy}) async {
if (key.isEmpty) {
throw ArgumentError('Key cannot be empty');
}
try {
final cacheItem = await _cacheAdapter.get(key);
if (cacheItem == null) {
// Record cache miss in analytics
_analytics.recordMiss(key);
// If a refresh callback is provided, use it to get fresh data
if (refreshCallback != null) {
final freshValue = await refreshCallback();
await put(key, freshValue, policy: policy);
return freshValue;
}
return null;
}
if (cacheItem.isExpired) {
// Record cache miss in analytics
_analytics.recordMiss(key);
await delete(key);
// If a refresh callback is provided, use it to get fresh data
if (refreshCallback != null) {
final freshValue = await refreshCallback();
await put(key, freshValue, policy: policy);
return freshValue;
}
return null;
}
// Check if the item is stale and needs refreshing
final effectivePolicy = policy ?? CachePolicy.defaultPolicy;
if (refreshCallback != null &&
effectivePolicy.staleTime != null &&
cacheItem.isStale(effectivePolicy.staleTime!)) {
// Handle different refresh strategies
switch (effectivePolicy.refreshStrategy) {
case RefreshStrategy.backgroundRefresh:
// Update in the background without blocking
_refreshInBackground(key, refreshCallback, effectivePolicy);
break;
case RefreshStrategy.immediateRefresh:
// Refresh immediately and return the fresh value
final freshValue = await refreshCallback();
await put(key, freshValue, policy: effectivePolicy);
return freshValue;
case RefreshStrategy.never:
// Do nothing, just use the cached value
break;
}
}
// Update sliding expiry if needed
// Decompress the value if it's compressed
T? resultValue;
if (cacheItem.isCompressed &&
_compression != null &&
cacheItem.value is String) {
try {
final decompressedValue =
_compression.decompressString(cacheItem.value as String);
resultValue = decompressedValue as T?;
_log.fine('Decompressed value for key $key');
} catch (e) {
_log.warning('Failed to decompress value for key $key: $e');
resultValue = cacheItem.value as T?;
}
} else {
resultValue = cacheItem.value as T?;
}
if (cacheItem.slidingExpiry != null) {
final updatedCacheItem = cacheItem.updateExpiry();
await _cacheAdapter.put(key, updatedCacheItem);
// Record cache hit in analytics
_analytics.recordHit(key);
return resultValue;
}
// Update access metadata
final updatedCacheItem = cacheItem.updateExpiry();
if (updatedCacheItem != cacheItem) {
await _cacheAdapter.put(key, updatedCacheItem);
}
// Record cache hit in analytics
_analytics.recordHit(key);
return resultValue;
} on HiveError catch (e) {
_log.severe('Failed to get data from cache (HiveError): $e');
throw CacheException('Failed to get data from cache: ${e.message}');
} catch (e) {
_log.severe('Failed to get data from cache (Unknown Error): $e');
throw CacheException('Failed to get data from cache: $e');
}
}