fix: db compaction run (#1156)

These fixes were taken from https://github.com/arneman/EOS/tree/refactor/economic-objective.

1. Fix compaction job registration bug in eos.py:
   - compact_eos_database was registered with save_eos_database function
   - Now correctly calls compact_eos_database()
   - Root cause: compaction/vacuum never ran, allowing records to grow unbounded

2. Optimize db_iterate_records() from O(n) to O(log n):
   - Previous: linear scan from index 0 for every key_to_array() call
   - Now: uses bisect_left on _db_sorted_timestamps to skip to start position
   - Critical for large datasets: 9400 measurement records → 94s/call overhead → 5 calls/optimization

3. Reduce compaction_interval_sec default from 604800s (7 days) to 3600s (1 hour):
   - With 7-day interval: 65000 records accumulate before first cleanup
   - Bridge pushes ~1 record/9s (grid_export_emr)
   - Hourly compaction + 2h data window → stable ~950 records

Signed-off-by: Bobby Noelte <b0661n0e17e@gmail.com>
This commit is contained in:
Bobby Noelte
2026-07-20 12:59:08 +02:00
committed by GitHub
parent bd506d24d7
commit 2ed04d5573
11 changed files with 30 additions and 21 deletions

View File

@@ -109,7 +109,7 @@ class DatabaseCommonSettings(SettingsBaseModel):
)
compaction_interval_sec: Optional[int] = Field(
default=7 * 24 * 3600, # weekly
default=3600, # hourly
ge=0,
json_schema_extra={
"description": (
@@ -117,7 +117,7 @@ class DatabaseCommonSettings(SettingsBaseModel):
"Compaction downsamples old records to reduce storage while retaining "
"coverage. Set to None to disable automatic compaction."
),
"examples": [604800], # 1 week
"examples": [3600], # 1 hour
},
)

View File

@@ -1603,7 +1603,14 @@ class DatabaseRecordProtocolMixin(
end_timestamp=end_timestamp,
)
for record in self.records:
# Use bisect to find the first record at or after start_timestamp.
# _db_sorted_timestamps and self.records are always kept in parallel
# (same insertion index), so bisect_left gives the correct slice start.
start_idx = 0
if start_timestamp and not isinstance(start_timestamp, _DatabaseTimestampUnbound):
start_idx = bisect.bisect_left(self._db_sorted_timestamps, start_timestamp)
for record in self.records[start_idx:]:
record_date_time_timestamp = DatabaseTimestamp.from_datetime(record.date_time)
if start_timestamp and record_date_time_timestamp < start_timestamp:
@@ -1702,7 +1709,7 @@ class DatabaseRecordProtocolMixin(
f"Vacuum requested for database '{self.db_namespace()}' but keep limit is infinite."
)
return 0
keep_hours = keep_duration.hours
keep_hours = int(keep_duration.total_seconds() // 3600)
if keep_hours is not None:
_, db_max = await self.db_timestamp_range()