feat(etag): implement Phase 3.1 ETag support for efficient content fetching

- Add DailyNotificationETagManager for Android with conditional request handling
- Add DailyNotificationETagManager for iOS with URLSession integration
- Update DailyNotificationFetcher with ETag manager integration
- Implement If-None-Match header support for conditional requests
- Add 304 Not Modified response handling for cached content
- Add ETag storage and validation with TTL management
- Add network efficiency metrics and cache statistics
- Add conditional request logic with fallback handling
- Add ETag cache management and cleanup methods
- Add phase3-1-etag-support.ts usage examples

This implements Phase 3.1 ETag support for network optimization:
- Conditional requests with If-None-Match headers
- 304 Not Modified response handling for bandwidth savings
- ETag caching with 24-hour TTL for efficient storage
- Network metrics tracking cache hit ratios and efficiency
- Graceful fallback when ETag requests fail
- Comprehensive cache management and cleanup
- Cross-platform implementation (Android + iOS)

Files: 4 changed, 800+ insertions(+)
This commit is contained in:
Matthew Raymer
2025-09-09 03:19:54 +00:00
parent 69aca905e1
commit 13905db3e4
4 changed files with 1341 additions and 34 deletions

View File

@@ -0,0 +1,317 @@
/**
* Phase 3.1 ETag Support Implementation Usage Example
*
* Demonstrates ETag-based conditional requests for efficient content fetching
* Shows 304 Not Modified handling, cache management, and network metrics
*
* @author Matthew Raymer
* @version 1.0.0
*/
import { DailyNotification } from '@timesafari/daily-notification-plugin';
/**
* Example: Configure ETag support for efficient fetching
*/
async function configureETagSupport() {
try {
console.log('Configuring ETag support for efficient fetching...');
// Configure with ETag support
await DailyNotification.configure({
storage: 'shared',
ttlSeconds: 1800, // 30 minutes TTL
prefetchLeadMinutes: 15,
enableETagSupport: true // Enable ETag conditional requests
});
console.log('✅ ETag support configured');
// The plugin will now:
// - Send If-None-Match headers with cached ETags
// - Handle 304 Not Modified responses efficiently
// - Cache ETag values for future requests
// - Track network efficiency metrics
} catch (error) {
console.error('❌ ETag support configuration failed:', error);
}
}
/**
* Example: Demonstrate ETag conditional requests
*/
async function demonstrateETagConditionalRequests() {
try {
console.log('Demonstrating ETag conditional requests...');
// Configure ETag support
await configureETagSupport();
// First request - will fetch content and cache ETag
console.log('📡 First request (no ETag cached)...');
await DailyNotification.scheduleDailyNotification({
url: 'https://api.example.com/daily-content',
time: '09:00',
title: 'Daily Update',
body: 'Your daily notification is ready'
});
console.log('✅ First request completed - ETag cached');
// Second request - will use conditional request
console.log('📡 Second request (ETag cached)...');
await DailyNotification.scheduleDailyNotification({
url: 'https://api.example.com/daily-content',
time: '09:15',
title: 'Daily Update',
body: 'Your daily notification is ready'
});
console.log('✅ Second request completed - conditional request used');
// The plugin will:
// - Send If-None-Match header with cached ETag
// - Receive 304 Not Modified if content unchanged
// - Use cached content instead of downloading
// - Update metrics to track efficiency
} catch (error) {
console.error('❌ ETag conditional requests demonstration failed:', error);
}
}
/**
* Example: Check network efficiency metrics
*/
async function checkNetworkEfficiencyMetrics() {
try {
console.log('Checking network efficiency metrics...');
// Configure ETag support
await configureETagSupport();
// Make some requests to generate metrics
await demonstrateETagConditionalRequests();
// Get network metrics
const metrics = await DailyNotification.getNetworkMetrics();
console.log('📊 Network Efficiency Metrics:');
console.log(` Total Requests: ${metrics.totalRequests}`);
console.log(` Cached Responses: ${metrics.cachedResponses}`);
console.log(` Network Responses: ${metrics.networkResponses}`);
console.log(` Errors: ${metrics.errors}`);
console.log(` Cache Hit Ratio: ${(metrics.cacheHitRatio * 100).toFixed(1)}%`);
// Example output:
// Total Requests: 4
// Cached Responses: 2
// Network Responses: 2
// Errors: 0
// Cache Hit Ratio: 50.0%
if (metrics.cacheHitRatio > 0.5) {
console.log('✅ Good cache efficiency - ETag support is working well');
} else {
console.log('⚠️ Low cache efficiency - content may be changing frequently');
}
} catch (error) {
console.error('❌ Network efficiency metrics check failed:', error);
}
}
/**
* Example: Manage ETag cache
*/
async function manageETagCache() {
try {
console.log('Managing ETag cache...');
// Configure ETag support
await configureETagSupport();
// Get cache statistics
const cacheStats = await DailyNotification.getCacheStatistics();
console.log('🗄️ ETag Cache Statistics:');
console.log(` Total ETags: ${cacheStats.totalETags}`);
console.log(` Valid ETags: ${cacheStats.validETags}`);
console.log(` Expired ETags: ${cacheStats.expiredETags}`);
// Clean expired ETags
if (cacheStats.expiredETags > 0) {
console.log('🧹 Cleaning expired ETags...');
await DailyNotification.cleanExpiredETags();
console.log('✅ Expired ETags cleaned');
}
// Reset metrics
console.log('🔄 Resetting network metrics...');
await DailyNotification.resetNetworkMetrics();
console.log('✅ Network metrics reset');
} catch (error) {
console.error('❌ ETag cache management failed:', error);
}
}
/**
* Example: Handle ETag failures gracefully
*/
async function handleETagFailuresGracefully() {
try {
console.log('Handling ETag failures gracefully...');
// Configure ETag support
await configureETagSupport();
// Schedule notification with potential ETag issues
await DailyNotification.scheduleDailyNotification({
url: 'https://api.example.com/unreliable-content',
time: '09:00',
title: 'Daily Update',
body: 'Your daily notification is ready'
});
console.log('✅ Notification scheduled with ETag fallback');
// The plugin will handle ETag failures by:
// - Falling back to full content fetch if ETag fails
// - Logging ETag errors for debugging
// - Continuing with notification scheduling
// - Updating error metrics
// Check metrics after potential failures
const metrics = await DailyNotification.getNetworkMetrics();
if (metrics.errors > 0) {
console.log(`⚠️ ${metrics.errors} ETag errors occurred - fallback used`);
} else {
console.log('✅ No ETag errors - all requests successful');
}
} catch (error) {
console.error('❌ ETag failure handling failed:', error);
}
}
/**
* Example: Monitor ETag performance over time
*/
async function monitorETagPerformance() {
try {
console.log('Monitoring ETag performance over time...');
// Configure ETag support
await configureETagSupport();
// Monitor performance over multiple requests
const monitoringInterval = setInterval(async () => {
try {
const metrics = await DailyNotification.getNetworkMetrics();
const cacheStats = await DailyNotification.getCacheStatistics();
console.log('📊 Performance Snapshot:');
console.log(` Cache Hit Ratio: ${(metrics.cacheHitRatio * 100).toFixed(1)}%`);
console.log(` Total Requests: ${metrics.totalRequests}`);
console.log(` Errors: ${metrics.errors}`);
console.log(` Valid ETags: ${cacheStats.validETags}`);
// Stop monitoring if we have enough data
if (metrics.totalRequests >= 10) {
clearInterval(monitoringInterval);
console.log('✅ Performance monitoring completed');
}
} catch (error) {
console.error('❌ Performance monitoring error:', error);
}
}, 5000); // Check every 5 seconds
// Make some requests to generate data
for (let i = 0; i < 5; i++) {
await DailyNotification.scheduleDailyNotification({
url: 'https://api.example.com/daily-content',
time: `09:${i.toString().padStart(2, '0')}`,
title: 'Daily Update',
body: 'Your daily notification is ready'
});
// Wait between requests
await new Promise(resolve => setTimeout(resolve, 2000));
}
} catch (error) {
console.error('❌ ETag performance monitoring failed:', error);
}
}
/**
* Example: Optimize content fetching with ETags
*/
async function optimizeContentFetchingWithETags() {
try {
console.log('Optimizing content fetching with ETags...');
// Configure ETag support
await configureETagSupport();
// Schedule multiple notifications for the same content
const notifications = [
{ time: '09:00', title: 'Morning Update' },
{ time: '12:00', title: 'Midday Update' },
{ time: '15:00', title: 'Afternoon Update' },
{ time: '18:00', title: 'Evening Update' }
];
for (const notification of notifications) {
await DailyNotification.scheduleDailyNotification({
url: 'https://api.example.com/daily-content', // Same URL
time: notification.time,
title: notification.title,
body: 'Your daily notification is ready'
});
console.log(`✅ Scheduled ${notification.title} at ${notification.time}`);
}
// Check final metrics
const metrics = await DailyNotification.getNetworkMetrics();
console.log('📊 Optimization Results:');
console.log(` Total Requests: ${metrics.totalRequests}`);
console.log(` Cached Responses: ${metrics.cachedResponses}`);
console.log(` Cache Hit Ratio: ${(metrics.cacheHitRatio * 100).toFixed(1)}%`);
// With ETag support, we should see:
// - First request: Network response (200 OK)
// - Subsequent requests: Cached responses (304 Not Modified)
// - High cache hit ratio (75%+)
// - Reduced bandwidth usage
// - Faster response times
if (metrics.cacheHitRatio >= 0.75) {
console.log('✅ Excellent optimization - ETag support is highly effective');
} else if (metrics.cacheHitRatio >= 0.5) {
console.log('✅ Good optimization - ETag support is working well');
} else {
console.log('⚠️ Limited optimization - content may be changing frequently');
}
} catch (error) {
console.error('❌ Content fetching optimization failed:', error);
}
}
// Export examples for use
export {
configureETagSupport,
demonstrateETagConditionalRequests,
checkNetworkEfficiencyMetrics,
manageETagCache,
handleETagFailuresGracefully,
monitorETagPerformance,
optimizeContentFetchingWithETags
};