320 lines
7.1 KiB
Markdown
320 lines
7.1 KiB
Markdown
# P3.1 Cursor Task Block — Performance Optimization & Metrics
|
|
|
|
**Purpose:** Ultra-compressed, mechanical execution steps for P3.1 only.
|
|
**Baseline:** `v1.0.11-p2.3-p1.5b-complete`
|
|
**Invariants:** See `docs/progress/P3-EXECUTION-CHECKLIST-MECHANICAL.md` section 0
|
|
|
|
---
|
|
|
|
## Preflight (Before Each Batch)
|
|
|
|
```bash
|
|
./ci/run.sh
|
|
# STOP IF FAILS
|
|
```
|
|
|
|
---
|
|
|
|
## Batch P3.1-A: Metrics Contract
|
|
|
|
**File:** `src/core/metrics.ts` (NEW)
|
|
|
|
**Action:** Create file with exact content:
|
|
|
|
```typescript
|
|
/**
|
|
* Core Metrics
|
|
*
|
|
* Performance metrics contract and lightweight collector.
|
|
* Platform-agnostic, no dependencies.
|
|
*
|
|
* @author Matthew Raymer
|
|
* @version 1.0.0
|
|
*/
|
|
|
|
export interface PerformanceMetric {
|
|
operation: string;
|
|
duration: number;
|
|
timestamp: number;
|
|
success: boolean;
|
|
metadata?: Record<string, unknown>;
|
|
}
|
|
|
|
export interface MetricsCollector {
|
|
record(metric: PerformanceMetric): void;
|
|
getMetrics(): PerformanceMetric[];
|
|
clear(): void;
|
|
}
|
|
|
|
export class InMemoryMetricsCollector implements MetricsCollector {
|
|
private metrics: PerformanceMetric[] = [];
|
|
private maxMetrics = 100;
|
|
|
|
record(metric: PerformanceMetric): void {
|
|
this.metrics.push(metric);
|
|
if (this.metrics.length > this.maxMetrics) {
|
|
this.metrics = this.metrics.slice(-this.maxMetrics);
|
|
}
|
|
}
|
|
|
|
getMetrics(): PerformanceMetric[] {
|
|
return [...this.metrics];
|
|
}
|
|
|
|
clear(): void {
|
|
this.metrics = [];
|
|
}
|
|
}
|
|
```
|
|
|
|
**File:** `src/core/index.ts` (UPDATE)
|
|
|
|
**Search:** `export * from './guards';`
|
|
|
|
**Action:** Add after: `export * from './metrics';`
|
|
|
|
**Verify:**
|
|
```bash
|
|
npm run build
|
|
./ci/run.sh
|
|
grep -r "@capacitor\|react\|fs\|path\|os" src/core/metrics.ts # Must be empty
|
|
```
|
|
|
|
---
|
|
|
|
## Batch P3.1-B: Instrument Scheduling
|
|
|
|
**File:** `src/web.ts` (UPDATE)
|
|
|
|
**Search:** `async createSchedule(_schedule: CreateScheduleInput): Promise<Schedule> {`
|
|
|
|
**Current:**
|
|
```typescript
|
|
async createSchedule(_schedule: CreateScheduleInput): Promise<Schedule> {
|
|
this.throwNotSupported();
|
|
}
|
|
```
|
|
|
|
**Replace with:**
|
|
```typescript
|
|
async createSchedule(_schedule: CreateScheduleInput): Promise<Schedule> {
|
|
const startTime = performance.now();
|
|
try {
|
|
this.throwNotSupported();
|
|
} catch (error) {
|
|
const duration = performance.now() - startTime;
|
|
if (this.observability) {
|
|
this.observability.logEvent('INFO', EVENT_CODES.SCHEDULE_UPDATE,
|
|
'Schedule creation attempted (not supported on web)',
|
|
{ duration, platform: 'web' });
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Import check:** Ensure `EVENT_CODES` imported:
|
|
```typescript
|
|
import { EVENT_CODES } from './core/events';
|
|
```
|
|
|
|
**Repeat for:**
|
|
- `updateSchedule()` (line ~338)
|
|
- `deleteSchedule()` (line ~342)
|
|
|
|
**Verify:**
|
|
```bash
|
|
npm run build
|
|
./ci/run.sh
|
|
```
|
|
|
|
---
|
|
|
|
## Batch P3.1-C: Instrument Recovery
|
|
|
|
**File:** `android/src/main/java/com/timesafari/dailynotification/ReactivationManager.kt` (UPDATE)
|
|
|
|
**Search:** `private suspend fun performColdStartRecovery(): RecoveryResult {`
|
|
|
|
**Add at start:**
|
|
```kotlin
|
|
val startTime = System.currentTimeMillis()
|
|
```
|
|
|
|
**Find return statement, add before:**
|
|
```kotlin
|
|
val duration = System.currentTimeMillis() - startTime
|
|
Log.i(TAG, "Cold start recovery completed: duration=${duration}ms, missed=$missedCount, rescheduled=$rescheduledCount, errors=$errors")
|
|
```
|
|
|
|
**Repeat for:** `performForceStopRecovery()`
|
|
|
|
**File:** `ios/Plugin/DailyNotificationReactivationManager.swift` (UPDATE)
|
|
|
|
**Search:** `func performColdStartRecovery() async throws -> RecoveryResult {`
|
|
|
|
**Add at start:**
|
|
```swift
|
|
let startTime = Date()
|
|
```
|
|
|
|
**Find return, add before:**
|
|
```swift
|
|
let duration = Date().timeIntervalSince(startTime) * 1000
|
|
os_log("Cold start recovery completed: duration=%.0fms, missed=%d, rescheduled=%d",
|
|
log: .default, type: .info, duration, missedCount, rescheduledCount)
|
|
```
|
|
|
|
**Verify:**
|
|
```bash
|
|
cd test-apps/android-test-app && ./gradlew :daily-notification-plugin:build
|
|
./ci/run.sh
|
|
```
|
|
|
|
---
|
|
|
|
## Batch P3.1-D: Instrument Database
|
|
|
|
**File:** `android/src/main/java/com/timesafari/dailynotification/ReactivationManager.kt` (UPDATE)
|
|
|
|
**Search:** `val enabledSchedules = try { db.scheduleDao().getEnabled() }`
|
|
|
|
**Replace with:**
|
|
```kotlin
|
|
val dbStartTime = System.currentTimeMillis()
|
|
val enabledSchedules = try {
|
|
db.scheduleDao().getEnabled()
|
|
} catch (e: Exception) {
|
|
Log.e(TAG, "Failed to load schedules from DB", e)
|
|
emptyList()
|
|
} finally {
|
|
val dbDuration = System.currentTimeMillis() - dbStartTime
|
|
if (dbDuration > 100) {
|
|
Log.w(TAG, "Database query slow: ${dbDuration}ms for getEnabled()")
|
|
} else {
|
|
Log.d(TAG, "Database query: ${dbDuration}ms, schedules=${enabledSchedules.size}")
|
|
}
|
|
}
|
|
```
|
|
|
|
**Verify:**
|
|
```bash
|
|
cd test-apps/android-test-app && ./gradlew :daily-notification-plugin:build
|
|
./ci/run.sh
|
|
```
|
|
|
|
---
|
|
|
|
## Batch P3.1-E: Performance Documentation
|
|
|
|
**File:** `docs/PERFORMANCE.md` (NEW)
|
|
|
|
**Action:** Create with exact content:
|
|
|
|
```markdown
|
|
# Performance Characteristics
|
|
|
|
**Purpose:** Expected performance characteristics and benchmarks.
|
|
**Owner:** Development Team
|
|
**Last Updated:** 2025-12-22
|
|
**Status:** active
|
|
|
|
## Expected Operation Times
|
|
|
|
### Scheduling Operations
|
|
- Schedule creation: < 50ms (typical), < 100ms (p95)
|
|
- Schedule update: < 50ms (typical), < 100ms (p95)
|
|
- Schedule deletion: < 50ms (typical), < 100ms (p95)
|
|
|
|
### Recovery Operations
|
|
- Cold start recovery: < 500ms (typical), < 1000ms (p95)
|
|
- Force stop recovery: < 500ms (typical), < 1000ms (p95)
|
|
- Boot recovery: < 1000ms (typical), < 2000ms (p95)
|
|
|
|
### Database Operations
|
|
- Query (getEnabled): < 50ms (typical), < 100ms (p95)
|
|
- Query (getById): < 10ms (typical), < 20ms (p95)
|
|
- Insert/Update: < 50ms (typical), < 100ms (p95)
|
|
|
|
## Memory Footprint
|
|
|
|
- In-memory metrics: ~10KB per 100 metrics
|
|
- Event logs: ~5KB per 100 events
|
|
- Total overhead: < 100KB (development), < 10KB (production)
|
|
|
|
## Platform-Specific Considerations
|
|
|
|
### iOS
|
|
- Background task limits: ~30 seconds
|
|
- CoreData migrations: typically < 100ms
|
|
|
|
### Android
|
|
- WorkManager limits: flexible (minutes)
|
|
- Room migrations: typically < 200ms
|
|
|
|
### Web
|
|
- No background execution limits
|
|
- No native database operations
|
|
|
|
## Measurement Methodology
|
|
|
|
Metrics use:
|
|
- `performance.now()` (Web/TypeScript)
|
|
- `System.currentTimeMillis()` (Android)
|
|
- `Date.timeIntervalSince()` (iOS)
|
|
|
|
All timings in milliseconds.
|
|
|
|
---
|
|
|
|
**See also:**
|
|
- [SYSTEM_INVARIANTS.md](./SYSTEM_INVARIANTS.md)
|
|
- [docs/progress/03-TEST-RUNS.md](./progress/03-TEST-RUNS.md)
|
|
```
|
|
|
|
**File:** `docs/00-INDEX.md` (UPDATE)
|
|
|
|
**Search:** `## Policy & Contracts (Executable)`
|
|
|
|
**Add link:**
|
|
```markdown
|
|
- [PERFORMANCE.md](./PERFORMANCE.md) — Performance characteristics and benchmarks
|
|
```
|
|
|
|
**Verify:**
|
|
```bash
|
|
./ci/run.sh
|
|
```
|
|
|
|
---
|
|
|
|
## P3.1 Acceptance
|
|
|
|
- [ ] `src/core/metrics.ts` exists
|
|
- [ ] Scheduling methods instrumented
|
|
- [ ] Recovery methods instrumented
|
|
- [ ] Database operations instrumented
|
|
- [ ] `docs/PERFORMANCE.md` created and linked
|
|
- [ ] `./ci/run.sh` green
|
|
- [ ] No new dependencies
|
|
- [ ] Tests pass
|
|
|
|
---
|
|
|
|
## Update Progress Docs (After P3.1 Complete)
|
|
|
|
**Files:**
|
|
- `docs/progress/00-STATUS.md` — Mark P3.1 complete
|
|
- `docs/progress/01-CHANGELOG-WORK.md` — Add P3.1 entry
|
|
- `docs/progress/03-TEST-RUNS.md` — Add performance metrics note
|
|
|
|
**Verify:**
|
|
```bash
|
|
./ci/run.sh
|
|
```
|
|
|
|
---
|
|
|
|
**Next:** Proceed to P3.2 (Observability) after P3.1 is green.
|
|
|