@@ -1142,13 +1142,15 @@ final class HealthKitService {
|
||
| 1142 | 1142 |
earliestDate: earliestDate, |
| 1143 | 1143 |
latestDate: latestDate |
| 1144 | 1144 |
) {
|
| 1145 |
- let verificationStartedAt = Date() |
|
| 1146 |
- try await archiveStore.markVerification( |
|
| 1145 |
+ captureTimings.finalizeElapsedSeconds += try await finalizeArchiveVerification( |
|
| 1147 | 1146 |
sampleType: sampleType, |
| 1148 |
- verifiedAt: Date(), |
|
| 1149 |
- observationID: archiveObservationID |
|
| 1147 |
+ typeIdentifier: typeIdentifier, |
|
| 1148 |
+ recordCount: unchanged.totalCount, |
|
| 1149 |
+ progressStarted: progressStarted, |
|
| 1150 |
+ processedEventCount: processedEventCount, |
|
| 1151 |
+ archiveObservationID: archiveObservationID, |
|
| 1152 |
+ progress: progress |
|
| 1150 | 1153 |
) |
| 1151 |
- captureTimings.finalizeElapsedSeconds += Date().timeIntervalSince(verificationStartedAt) |
|
| 1152 | 1154 |
progress?.updateBlockProgress( |
| 1153 | 1155 |
typeIdentifier, |
| 1154 | 1156 |
detail: "No HealthKit delta", |
@@ -1273,9 +1275,15 @@ final class HealthKitService {
|
||
| 1273 | 1275 |
) |
| 1274 | 1276 |
} |
| 1275 | 1277 |
|
| 1276 |
- let verificationStartedAt = Date() |
|
| 1277 |
- try await archiveStore.markVerification(sampleType: sampleType, verifiedAt: Date(), observationID: archiveObservationID) |
|
| 1278 |
- captureTimings.finalizeElapsedSeconds += Date().timeIntervalSince(verificationStartedAt) |
|
| 1278 |
+ captureTimings.finalizeElapsedSeconds += try await finalizeArchiveVerification( |
|
| 1279 |
+ sampleType: sampleType, |
|
| 1280 |
+ typeIdentifier: typeIdentifier, |
|
| 1281 |
+ recordCount: recordMap.count, |
|
| 1282 |
+ progressStarted: progressStarted, |
|
| 1283 |
+ processedEventCount: processedEventCount, |
|
| 1284 |
+ archiveObservationID: archiveObservationID, |
|
| 1285 |
+ progress: progress |
|
| 1286 |
+ ) |
|
| 1279 | 1287 |
|
| 1280 | 1288 |
let sortedRecordsStartedAt = Date() |
| 1281 | 1289 |
let sortedKeys = recordMap.keys.sorted {
|
@@ -1465,9 +1473,15 @@ final class HealthKitService {
|
||
| 1465 | 1473 |
) |
| 1466 | 1474 |
) |
| 1467 | 1475 |
|
| 1468 |
- let verificationStartedAt = Date() |
|
| 1469 |
- try await archiveStore.markVerification(sampleType: sampleType, verifiedAt: Date(), observationID: archiveObservationID) |
|
| 1470 |
- captureTimings.finalizeElapsedSeconds += Date().timeIntervalSince(verificationStartedAt) |
|
| 1476 |
+ captureTimings.finalizeElapsedSeconds += try await finalizeArchiveVerification( |
|
| 1477 |
+ sampleType: sampleType, |
|
| 1478 |
+ typeIdentifier: typeIdentifier, |
|
| 1479 |
+ recordCount: recordCount, |
|
| 1480 |
+ progressStarted: progressStarted, |
|
| 1481 |
+ processedEventCount: processedEventCount, |
|
| 1482 |
+ archiveObservationID: archiveObservationID, |
|
| 1483 |
+ progress: progress |
|
| 1484 |
+ ) |
|
| 1471 | 1485 |
|
| 1472 | 1486 |
guard recordCount > 0 || anchor != nil else {
|
| 1473 | 1487 |
return SampleDistribution( |
@@ -1564,6 +1578,50 @@ final class HealthKitService {
|
||
| 1564 | 1578 |
return Double(processedCount) / elapsedSeconds |
| 1565 | 1579 |
} |
| 1566 | 1580 |
|
| 1581 |
+ private func finalizeArchiveVerification( |
|
| 1582 |
+ sampleType: HKSampleType, |
|
| 1583 |
+ typeIdentifier: String, |
|
| 1584 |
+ recordCount: Int, |
|
| 1585 |
+ progressStarted: Date, |
|
| 1586 |
+ processedEventCount: Int, |
|
| 1587 |
+ archiveObservationID: Int64, |
|
| 1588 |
+ progress: SnapshotFetchProgress? |
|
| 1589 |
+ ) async throws -> TimeInterval {
|
|
| 1590 |
+ let elapsedBeforeFinalize = Date().timeIntervalSince(progressStarted) |
|
| 1591 |
+ progress?.updateBlockProgress( |
|
| 1592 |
+ typeIdentifier, |
|
| 1593 |
+ detail: "Finalizing archive aggregates", |
|
| 1594 |
+ recordCount: recordCount, |
|
| 1595 |
+ elapsedSeconds: elapsedBeforeFinalize, |
|
| 1596 |
+ samplesPerSecond: Self.samplesPerSecond( |
|
| 1597 |
+ processedCount: processedEventCount, |
|
| 1598 |
+ elapsedSeconds: elapsedBeforeFinalize |
|
| 1599 |
+ ) |
|
| 1600 |
+ ) |
|
| 1601 |
+ |
|
| 1602 |
+ let verificationStartedAt = Date() |
|
| 1603 |
+ try await archiveStore.markVerification( |
|
| 1604 |
+ sampleType: sampleType, |
|
| 1605 |
+ verifiedAt: Date(), |
|
| 1606 |
+ observationID: archiveObservationID |
|
| 1607 |
+ ) |
|
| 1608 |
+ let finalizeElapsed = Date().timeIntervalSince(verificationStartedAt) |
|
| 1609 |
+ |
|
| 1610 |
+ let elapsedAfterFinalize = Date().timeIntervalSince(progressStarted) |
|
| 1611 |
+ progress?.updateBlockProgress( |
|
| 1612 |
+ typeIdentifier, |
|
| 1613 |
+ detail: "Archive aggregates finalized", |
|
| 1614 |
+ recordCount: recordCount, |
|
| 1615 |
+ elapsedSeconds: elapsedAfterFinalize, |
|
| 1616 |
+ samplesPerSecond: Self.samplesPerSecond( |
|
| 1617 |
+ processedCount: processedEventCount, |
|
| 1618 |
+ elapsedSeconds: elapsedAfterFinalize |
|
| 1619 |
+ ) |
|
| 1620 |
+ ) |
|
| 1621 |
+ |
|
| 1622 |
+ return finalizeElapsed |
|
| 1623 |
+ } |
|
| 1624 |
+ |
|
| 1567 | 1625 |
private static func pageProgressDetail( |
| 1568 | 1626 |
operation: String, |
| 1569 | 1627 |
pageNumber: Int, |
@@ -1712,6 +1712,7 @@ actor SQLiteHealthArchiveStore: HealthArchiveStore {
|
||
| 1712 | 1712 |
""", db: db) |
| 1713 | 1713 |
try execute("CREATE INDEX IF NOT EXISTS idx_visibility_open_ranges ON sample_visibility_ranges(last_observation_id)", db: db)
|
| 1714 | 1714 |
try execute("CREATE INDEX IF NOT EXISTS idx_visibility_sample_open ON sample_visibility_ranges(sample_id, last_observation_id)", db: db)
|
| 1715 |
+ try execute("CREATE INDEX IF NOT EXISTS idx_visibility_sample_open_version ON sample_visibility_ranges(sample_id, last_observation_id, version_id)", db: db)
|
|
| 1715 | 1716 |
try execute("CREATE INDEX IF NOT EXISTS idx_visibility_sample_version_open ON sample_visibility_ranges(sample_id, version_id, last_observation_id)", db: db)
|
| 1716 | 1717 |
try execute("CREATE INDEX IF NOT EXISTS idx_visibility_point_lookup ON sample_visibility_ranges(first_observation_id, last_observation_id)", db: db)
|
| 1717 | 1718 |
try execute("""
|
@@ -2989,6 +2990,11 @@ actor SQLiteHealthArchiveStore: HealthArchiveStore {
|
||
| 2989 | 2990 |
db: OpaquePointer? |
| 2990 | 2991 |
) throws -> [ArchiveV2DailyAggregateRow] {
|
| 2991 | 2992 |
let sql = """ |
| 2993 |
+ WITH typed_samples AS ( |
|
| 2994 |
+ SELECT id |
|
| 2995 |
+ FROM samples INDEXED BY idx_samples_type_id |
|
| 2996 |
+ WHERE sample_type_id = ? |
|
| 2997 |
+ ) |
|
| 2992 | 2998 |
SELECT |
| 2993 | 2999 |
CAST(((v.start_date + ?) / 86400) AS INTEGER) * 86400 - ? AS bucket_start, |
| 2994 | 3000 |
CAST(((v.start_date + ?) / 86400) AS INTEGER) * 86400 - ? + 86400 AS bucket_end, |
@@ -2996,21 +3002,20 @@ actor SQLiteHealthArchiveStore: HealthArchiveStore {
|
||
| 2996 | 3002 |
SUM(v.numeric_value), |
| 2997 | 3003 |
MAX(v.numeric_value), |
| 2998 | 3004 |
v.source_revision_id |
| 2999 |
- FROM samples s |
|
| 3000 |
- JOIN sample_visibility_ranges r |
|
| 3005 |
+ FROM typed_samples s |
|
| 3006 |
+ JOIN sample_visibility_ranges r INDEXED BY idx_visibility_sample_open_version |
|
| 3001 | 3007 |
ON r.sample_id = s.id |
| 3002 | 3008 |
AND r.last_observation_id IS NULL |
| 3003 | 3009 |
JOIN sample_versions v ON v.id = r.version_id |
| 3004 |
- WHERE s.sample_type_id = ? |
|
| 3005 | 3010 |
GROUP BY bucket_start, bucket_end, v.source_revision_id |
| 3006 | 3011 |
ORDER BY bucket_start ASC, v.source_revision_id ASC |
| 3007 | 3012 |
""" |
| 3008 | 3013 |
return try withStatement(sql, db: db) { statement in
|
| 3009 |
- sqlite3_bind_double(statement, 1, Double(secondsFromGMT)) |
|
| 3014 |
+ bindInt64(sampleTypeID, to: 1, in: statement) |
|
| 3010 | 3015 |
sqlite3_bind_double(statement, 2, Double(secondsFromGMT)) |
| 3011 | 3016 |
sqlite3_bind_double(statement, 3, Double(secondsFromGMT)) |
| 3012 | 3017 |
sqlite3_bind_double(statement, 4, Double(secondsFromGMT)) |
| 3013 |
- bindInt64(sampleTypeID, to: 5, in: statement) |
|
| 3018 |
+ sqlite3_bind_double(statement, 5, Double(secondsFromGMT)) |
|
| 3014 | 3019 |
|
| 3015 | 3020 |
var rows: [ArchiveV2DailyAggregateRow] = [] |
| 3016 | 3021 |
while sqlite3_step(statement) == SQLITE_ROW {
|
@@ -3048,11 +3053,13 @@ actor SQLiteHealthArchiveStore: HealthArchiveStore {
|
||
| 3048 | 3053 |
db: OpaquePointer? |
| 3049 | 3054 |
) throws -> (appeared: Int, disappeared: Int, representationChanged: Int) {
|
| 3050 | 3055 |
let sql = """ |
| 3051 |
- SELECT event_kind, COUNT(*) |
|
| 3052 |
- FROM sample_observation_events e |
|
| 3053 |
- JOIN samples s ON s.id = e.sample_id |
|
| 3054 |
- WHERE e.observation_id = ? AND s.sample_type_id = ? |
|
| 3055 |
- GROUP BY event_kind |
|
| 3056 |
+ SELECT e.event_kind, COUNT(*) |
|
| 3057 |
+ FROM samples s INDEXED BY idx_samples_type_id |
|
| 3058 |
+ JOIN sample_observation_events e INDEXED BY idx_events_sample |
|
| 3059 |
+ ON e.sample_id = s.id |
|
| 3060 |
+ AND e.observation_id = ? |
|
| 3061 |
+ WHERE s.sample_type_id = ? |
|
| 3062 |
+ GROUP BY e.event_kind |
|
| 3056 | 3063 |
""" |
| 3057 | 3064 |
return try withStatement(sql, db: db) { statement in
|
| 3058 | 3065 |
bindInt64(observationID, to: 1, in: statement) |
@@ -3080,13 +3087,17 @@ actor SQLiteHealthArchiveStore: HealthArchiveStore {
|
||
| 3080 | 3087 |
|
| 3081 | 3088 |
private func visibleAggregate(sampleTypeID: Int64, db: OpaquePointer?) throws -> ArchiveV2VisibleAggregate {
|
| 3082 | 3089 |
let sql = """ |
| 3090 |
+ WITH typed_samples AS ( |
|
| 3091 |
+ SELECT id |
|
| 3092 |
+ FROM samples INDEXED BY idx_samples_type_id |
|
| 3093 |
+ WHERE sample_type_id = ? |
|
| 3094 |
+ ) |
|
| 3083 | 3095 |
SELECT COUNT(*), MIN(v.start_date), MAX(v.end_date), SUM(v.numeric_value), MAX(v.numeric_value) |
| 3084 |
- FROM samples s |
|
| 3085 |
- JOIN sample_visibility_ranges r |
|
| 3096 |
+ FROM typed_samples s |
|
| 3097 |
+ JOIN sample_visibility_ranges r INDEXED BY idx_visibility_sample_open_version |
|
| 3086 | 3098 |
ON r.sample_id = s.id |
| 3087 | 3099 |
AND r.last_observation_id IS NULL |
| 3088 | 3100 |
JOIN sample_versions v ON v.id = r.version_id |
| 3089 |
- WHERE s.sample_type_id = ? |
|
| 3090 | 3101 |
""" |
| 3091 | 3102 |
return try withStatement(sql, db: db) { statement in
|
| 3092 | 3103 |
bindInt64(sampleTypeID, to: 1, in: statement) |