feat: add unified cache policy primitives
This commit is contained in:
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
class CacheEntry<T> {
|
||||
final T value;
|
||||
final DateTime fetchedAt;
|
||||
|
||||
const CacheEntry({required this.value, required this.fetchedAt});
|
||||
}
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
class CacheKey {
|
||||
final String value;
|
||||
|
||||
const CacheKey(this.value);
|
||||
|
||||
@override
|
||||
String toString() => value;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other is CacheKey && other.value == value);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => value.hashCode;
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
class CacheDecision {
|
||||
final bool canUseCached;
|
||||
final bool shouldRefreshInBackground;
|
||||
final bool mustBlockForNetwork;
|
||||
|
||||
const CacheDecision({
|
||||
required this.canUseCached,
|
||||
required this.shouldRefreshInBackground,
|
||||
required this.mustBlockForNetwork,
|
||||
});
|
||||
}
|
||||
|
||||
class CachePolicy {
|
||||
final Duration softTtl;
|
||||
final Duration hardTtl;
|
||||
final Duration minRefreshInterval;
|
||||
|
||||
const CachePolicy({
|
||||
required this.softTtl,
|
||||
required this.hardTtl,
|
||||
this.minRefreshInterval = Duration.zero,
|
||||
});
|
||||
|
||||
CacheDecision evaluate({required DateTime now, required DateTime fetchedAt}) {
|
||||
final age = now.difference(fetchedAt);
|
||||
if (age >= hardTtl) {
|
||||
return const CacheDecision(
|
||||
canUseCached: false,
|
||||
shouldRefreshInBackground: false,
|
||||
mustBlockForNetwork: true,
|
||||
);
|
||||
}
|
||||
|
||||
if (age >= softTtl) {
|
||||
final shouldRefresh = age >= minRefreshInterval;
|
||||
return CacheDecision(
|
||||
canUseCached: true,
|
||||
shouldRefreshInBackground: shouldRefresh,
|
||||
mustBlockForNetwork: false,
|
||||
);
|
||||
}
|
||||
|
||||
return const CacheDecision(
|
||||
canUseCached: true,
|
||||
shouldRefreshInBackground: false,
|
||||
mustBlockForNetwork: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:social_app/core/cache/cache_policy.dart';
|
||||
|
||||
void main() {
|
||||
test('soft expired should allow stale read with background refresh', () {
|
||||
final now = DateTime(2026, 3, 20, 12);
|
||||
final policy = CachePolicy(
|
||||
softTtl: const Duration(minutes: 2),
|
||||
hardTtl: const Duration(minutes: 30),
|
||||
minRefreshInterval: const Duration(minutes: 1),
|
||||
);
|
||||
|
||||
final fetchedAt = now.subtract(const Duration(minutes: 3));
|
||||
final decision = policy.evaluate(now: now, fetchedAt: fetchedAt);
|
||||
expect(decision.canUseCached, true);
|
||||
expect(decision.shouldRefreshInBackground, true);
|
||||
expect(decision.mustBlockForNetwork, false);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user