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