akkudoktoreos.core.databaseabc.DatabaseRecordProtocolMixin
- class akkudoktoreos.core.databaseabc.DatabaseRecordProtocolMixin
Bases:
ConfigMixin,DatabaseMixin,Generic[T_Record]Database Record Protocol Mixin.
Completely manages in memory records and database storage.
Expects records with date_time (DatabaseTimestamp) property and the a record list in self.records of the derived class.
DatabaseRecordProtocolMixin expects the derived classes to be singletons.
- __init__()
Methods
__init__()db_compact([compact_tiers])Apply tiered compaction policy to all records in this namespace.
Compaction tiers as (age_threshold, target_interval) pairs.
Return total logical number of records.
db_delete_records([start_timestamp, ...])db_generate_timestamps(start_timestamp, ...)Generate database timestamps using fixed absolute time stepping.
db_get_record(target_timestamp, *[, time_window])Get the record at or nearest to the specified timestamp.
Get comprehensive statistics about database storage.
Return the initial time window used for database loading.
db_insert_record(record, *[, mark_dirty])db_iterate_records([start_timestamp, ...])Iterate records in requested range.
Duration for which database records should be retained.
db_load_records([start_timestamp, end_timestamp])Load records from database into memory.
db_mark_dirty_record(record)Namespace of database.
db_next_timestamp(timestamp)Find the smallest timestamp > given timestamp.
db_previous_timestamp(timestamp)Find the largest timestamp < given timestamp.
Get the timestamp range of records in database.
db_vacuum([keep_hours, keep_timestamp])Remove old records from database to free space.
model_post_init(...)Initialize DB state attributes immediately after Pydantic construction.
Attributes
- records: list[T_Record]
- db_initial_time_window() Duration | None
Return the initial time window used for database loading.
This window defines the initial symmetric time span around a target datetime that should be loaded from the database when no explicit search time window is specified. It serves as a loading hint and may be expanded by the caller if no records are found within the initial range.
Subclasses may override this method to provide a domain-specific default.
- Returns:
The initial loading time window as a Duration, or
Noneto indicate that no initial window constraint should be applied.
- model_post_init(_DatabaseRecordProtocolMixin__context: Any) None
Initialize DB state attributes immediately after Pydantic construction.
- db_previous_timestamp(timestamp: DatabaseTimestamp) DatabaseTimestamp | None
Find the largest timestamp < given timestamp.
Search memory-first, then fallback to database if necessary.
- db_next_timestamp(timestamp: DatabaseTimestamp) DatabaseTimestamp | None
Find the smallest timestamp > given timestamp.
Search memory-first, then fallback to database if necessary.
- db_keep_duration() Duration | None
Duration for which database records should be retained.
Used when removing old records from database to free space.
Defaults to general database configuration.
May be provided by derived class.
- Returns:
Duration or None (forever).
- db_namespace() str
Namespace of database.
To be implemented by derived class.
- property db_enabled: bool
- db_timestamp_range() tuple[DatabaseTimestamp | None, DatabaseTimestamp | None]
Get the timestamp range of records in database.
Regards records in storage plus extra records in memory.
- db_generate_timestamps(start_timestamp: DatabaseTimestamp, values_count: int, interval: Duration | None = None) Iterator[DatabaseTimestamp]
Generate database timestamps using fixed absolute time stepping.
The iterator advances strictly in UTC, guaranteeing constant spacing in seconds across daylight saving transitions.
Returned database timestamps are in UTC. This avoids ambiguity during fall-back transitions and prevents accidental overwriting when inserting into UTC-normalized storage backends.
- Parameters:
start_timestamp (DatabaseTimestamp) – Starting database timestamp.
values_count (int) – Number of timestamps to generate.
interval (Optional[Duration]) – Fixed duration between timestamps. Defaults to 1 hour if not provided.
- Yields:
DatabaseTimestamp – UTC-based database timestamps.
- Raises:
ValueError – If values_count is negative.
- db_get_record(target_timestamp: DatabaseTimestamp, *, time_window: Duration | None | _DatabaseTimeWindowUnbound = None) T_Record | None
Get the record at or nearest to the specified timestamp.
The search strategies are:
None - exact match only.
UNBOUND_WINDOW - nearest record across all stored records.
Duration - nearest record within a symmetric window of this total width around target_timestamp.
- Parameters:
target_timestamp – The timestamp to search for.
time_window – Controls the search strategy (None, UNBOUND_WINDOW, Duration).
- Returns:
Exact match, nearest record within the window, or None.
- db_load_records(start_timestamp: DatabaseTimestamp | _DatabaseTimestampUnbound | None = None, end_timestamp: DatabaseTimestamp | _DatabaseTimestampUnbound | None = None) int
Load records from database into memory.
Merges database records into in-memory records while preserving: - Memory-only records - Sorted order - No duplicates (DB overwrites memory)
This requested load range is extended to include the first record < start_timestamp and the first record >= end_timestamp, so nearest-neighbor searches do not require additional DB lookups.
The _db_loaded_range is updated to reflect the total timestamp span currently present in memory after this method completes.
- Parameters:
start_timestamp – Load records from this timestamp (inclusive)
end_timestamp – Load records until this timestamp (exclusive)
- Returns:
Number of records loaded from database
Note
record.date_time shall be DateTime or None
- db_delete_records(start_timestamp: DatabaseTimestamp | _DatabaseTimestampUnbound | None = None, end_timestamp: DatabaseTimestamp | _DatabaseTimestampUnbound | None = None) int
- db_iterate_records(start_timestamp: DatabaseTimestamp | _DatabaseTimestampUnbound | None = None, end_timestamp: DatabaseTimestamp | _DatabaseTimestampUnbound | None = None) Iterator[T_Record]
Iterate records in requested range.
Ensures storage is loaded into memory first, then iterates over in-memory records only.
- db_save_records() int
- db_autosave() int
- db_vacuum(keep_hours: int | None = None, keep_timestamp: DatabaseTimestamp | _DatabaseTimestampUnbound | None = None) int
Remove old records from database to free space.
Semantics:
- keep_hours is relative to the DB’s max timestamp: cutoff = db_max - keep_hours, and records
with timestamp < cutoff are deleted.
keep_timestamp is an absolute cutoff; records with timestamp < cutoff are deleted (exclusive).
Uses self.keep_duration() if both of keep_hours and keep_timestamp are None.
- Parameters:
keep_hours – Keep only records from the last N hours (relative to the data’s max timestamp)
keep_timestamp – Keep only records from this timestamp on (absolute cutoff)
- Returns:
Number of records deleted
- db_count_records() int
Return total logical number of records.
Memory is authoritative. If DB is enabled but not fully loaded, we conservatively include storage-only records.
- db_get_stats() dict
Get comprehensive statistics about database storage.
- Returns:
Dictionary with statistics
- db_compact_tiers() list[tuple[Duration, Duration]]
Compaction tiers as (age_threshold, target_interval) pairs.
Records older than age_threshold are downsampled to target_interval. Tiers must be ordered from shortest to longest age threshold.
Default policy:
older than 2 hours → 15 min resolution
older than 14 days → 1 hour resolution
Return empty list to disable compaction entirely. Override in derived classes for domain-specific behaviour.
Example override to disable:
Example override for price data (already at 15 min, skip first tier):
- config = ConfigEOS(general=GeneralSettings(home_assistant_addon=False, version='0.2.0.dev58204789', data_folder_path=Path('/home/docs/.local/share/net.akkudoktor.eos'), data_output_subpath='output', latitude=52.52, longitude=13.405, timezone='Europe/Berlin', data_output_path=Path('/home/docs/.local/share/net.akkudoktor.eos/output'), config_folder_path=Path('/home/docs/checkouts/readthedocs.org/user_builds/akkudoktor-eos/checkouts/latest/docs'), config_file_path=Path('/home/docs/checkouts/readthedocs.org/user_builds/akkudoktor-eos/checkouts/latest/docs/EOS.config.json')), cache=CacheCommonSettings(subpath='cache', cleanup_interval=300.0), database=DatabaseCommonSettings(provider=None, compression_level=9, initial_load_window_h=None, keep_duration_h=None, autosave_interval_sec=10, compaction_interval_sec=604800, batch_size=100, providers=['LMDB', 'SQLite']), ems=EnergyManagementCommonSettings(startup_delay=5, interval=300.0, mode=None), logging=LoggingCommonSettings(console_level='INFO', file_level=None, file_path=Path('/home/docs/.local/share/net.akkudoktor.eos/output/eos.log')), devices=DevicesCommonSettings(batteries=None, max_batteries=None, electric_vehicles=None, max_electric_vehicles=None, inverters=None, max_inverters=None, home_appliances=None, max_home_appliances=None, measurement_keys=[]), measurement=MeasurementCommonSettings(historic_hours=17520, load_emr_keys=None, grid_export_emr_keys=None, grid_import_emr_keys=None, pv_production_emr_keys=None, keys=[]), optimization=OptimizationCommonSettings(horizon_hours=24, interval=3600, algorithm='GENETIC', genetic=GeneticCommonSettings(individuals=300, generations=400, seed=None, penalties=None), keys=[]), prediction=PredictionCommonSettings(hours=48, historic_hours=48), elecprice=ElecPriceCommonSettings(provider=None, charges_kwh=None, vat_rate=1.19, elecpriceimport=ElecPriceImportCommonSettings(import_file_path=None, import_json=None), energycharts=ElecPriceEnergyChartsCommonSettings(bidding_zone=<EnergyChartsBiddingZones.DE_LU: 'DE-LU'>), providers=['ElecPriceAkkudoktor', 'ElecPriceEnergyCharts', 'ElecPriceImport']), feedintariff=FeedInTariffCommonSettings(provider=None, provider_settings=FeedInTariffCommonProviderSettings(FeedInTariffFixed=None, FeedInTariffImport=None), providers=['FeedInTariffFixed', 'FeedInTariffImport']), load=LoadCommonSettings(provider=None, provider_settings=LoadCommonProviderSettings(LoadAkkudoktor=None, LoadVrm=None, LoadImport=None), providers=['LoadAkkudoktor', 'LoadAkkudoktorAdjusted', 'LoadVrm', 'LoadImport']), pvforecast=PVForecastCommonSettings(provider=None, provider_settings=PVForecastCommonProviderSettings(PVForecastImport=None, PVForecastVrm=None), planes=None, max_planes=0, providers=['PVForecastAkkudoktor', 'PVForecastVrm', 'PVForecastImport'], planes_peakpower=[], planes_azimuth=[], planes_tilt=[], planes_userhorizon=[], planes_inverter_paco=[]), weather=WeatherCommonSettings(provider=None, provider_settings=WeatherCommonProviderSettings(WeatherImport=None), providers=['BrightSky', 'ClearOutside', 'WeatherImport']), server=ServerCommonSettings(host='127.0.0.1', port=8503, verbose=False, startup_eosdash=True, eosdash_host=None, eosdash_port=None), utils=UtilsCommonSettings(), adapter=AdapterCommonSettings(provider=None, homeassistant=HomeAssistantAdapterCommonSettings(config_entity_ids=None, load_emr_entity_ids=None, grid_export_emr_entity_ids=None, grid_import_emr_entity_ids=None, pv_production_emr_entity_ids=None, device_measurement_entity_ids=None, device_instruction_entity_ids=None, solution_entity_ids=None, homeassistant_entity_ids=[], eos_solution_entity_ids=[], eos_device_instruction_entity_ids=[]), nodered=NodeREDAdapterCommonSettings(host='127.0.0.1', port=1880), providers=['HomeAssistant', 'NodeRED']))
- database = <akkudoktoreos.core.database.Database object>
- db_compact(compact_tiers: list[tuple[Duration, Duration]] | None = None) int
Apply tiered compaction policy to all records in this namespace.
Tiers are processed coarsest-first (longest age threshold first) to avoid compacting fine-grained data that an inner tier would immediately re-compact anyway.
- Parameters:
compact_tiers – Override tiers for this call. If None, uses db_compact_tiers(). Each entry is (age_threshold, target_interval), ordered shortest to longest age threshold.
- Returns:
Total number of original records deleted across all tiers.