e-data 2.0.2.dev135__tar.gz → 2.0.2.dev136__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {e_data-2.0.2.dev135/e_data.egg-info → e_data-2.0.2.dev136}/PKG-INFO +1 -1
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136/e_data.egg-info}/PKG-INFO +1 -1
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/database/controller.py +8 -1
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/tests/test_controller.py +18 -1
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/pyproject.toml +1 -1
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/LICENSE +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/MANIFEST.in +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/README.md +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/e_data.egg-info/SOURCES.txt +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/e_data.egg-info/dependency_links.txt +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/e_data.egg-info/requires.txt +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/e_data.egg-info/top_level.txt +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/__init__.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/cli.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/core/__init__.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/core/completion.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/core/const.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/core/utils.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/database/__init__.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/database/migrations/__init__.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/database/migrations/base.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/database/migrations/legacy_json_1_3_3.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/database/models.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/database/queries.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/database/utils.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/models/__init__.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/models/bill.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/models/data.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/models/supply.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/providers/__init__.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/providers/datadis.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/providers/redata.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/services/bill_service.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/services/data_service.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/tests/__init__.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/tests/test_completion.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/tests/test_datadis_connector.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/tests/test_incremental.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/tests/test_migrations.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/tests/test_redata_connector.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/tests/test_services.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/tests/test_utils.py +0 -0
- {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/setup.cfg +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
import logging
|
|
2
3
|
import os
|
|
3
4
|
import typing
|
|
@@ -93,6 +94,7 @@ class EdataDB:
|
|
|
93
94
|
cls._engine = create_async_engine(db_url, future=True)
|
|
94
95
|
event.listen(cls._engine.sync_engine, "connect", _set_sqlite_pragmas)
|
|
95
96
|
cls._instance._tables_initialized = False
|
|
97
|
+
cls._instance._tables_lock = asyncio.Lock()
|
|
96
98
|
elif db_url != cls._db_url:
|
|
97
99
|
raise ValueError("EdataDB already initialized with a different db_url")
|
|
98
100
|
return cls._instance
|
|
@@ -108,7 +110,12 @@ class EdataDB:
|
|
|
108
110
|
|
|
109
111
|
if self._tables_initialized:
|
|
110
112
|
return
|
|
111
|
-
|
|
113
|
+
# concurrent first calls (e.g. several websocket requests right after
|
|
114
|
+
# startup) would otherwise all see the index missing and race to create
|
|
115
|
+
# it, failing with "index ... already exists"
|
|
116
|
+
async with self._tables_lock:
|
|
117
|
+
if self._tables_initialized or not self.engine:
|
|
118
|
+
return
|
|
112
119
|
async with self.engine.begin() as conn:
|
|
113
120
|
await conn.run_sync(SQLModel.metadata.create_all)
|
|
114
121
|
await conn.run_sync(_create_missing_indexes)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Tests for the database controller."""
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
from collections.abc import AsyncIterator
|
|
4
5
|
from datetime import datetime, timedelta
|
|
5
6
|
|
|
@@ -89,3 +90,19 @@ async def test_add_bill_list_applies_overrides(db: EdataDB) -> None:
|
|
|
89
90
|
stored = await db.list_bill(CUPS, "hour")
|
|
90
91
|
assert len(stored) == 3
|
|
91
92
|
assert all(x.complete and x.confhash == "hash-b" for x in stored)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@pytest.mark.asyncio
|
|
96
|
+
async def test_concurrent_first_calls_do_not_race_index_creation(db: EdataDB) -> None:
|
|
97
|
+
# simulate a database created before the index existed, opened fresh
|
|
98
|
+
async with db.engine.begin() as conn:
|
|
99
|
+
await conn.exec_driver_sql("DROP INDEX ix_energy_cups_datetime")
|
|
100
|
+
db._tables_initialized = False
|
|
101
|
+
|
|
102
|
+
await asyncio.gather(*(db.get_last_energy(CUPS) for _ in range(10)))
|
|
103
|
+
|
|
104
|
+
async with db.engine.connect() as conn:
|
|
105
|
+
result = await conn.exec_driver_sql(
|
|
106
|
+
"SELECT name FROM sqlite_master WHERE name='ix_energy_cups_datetime'"
|
|
107
|
+
)
|
|
108
|
+
assert result.first() is not None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|