Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions burr/core/persistence.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,16 +446,18 @@ def is_initialized(self) -> bool:
return self._initialized

def list_app_ids(self, partition_key: Optional[str], **kwargs) -> list[str]:
"""Lists the app_ids for a given partition_key, each once, most recently saved first."""
partition_key = (
partition_key if partition_key is not None else SQLitePersister.PARTITION_KEY_DEFAULT
)

cursor = self.connection.cursor()
try:
cursor.execute(
f"SELECT DISTINCT app_id FROM {self.table_name} "
f"SELECT app_id FROM {self.table_name} "
f"WHERE partition_key = ? "
f"ORDER BY created_at DESC",
f"GROUP BY app_id "
f"ORDER BY MAX(created_at) DESC",
(partition_key,),
)
except sqlite3.OperationalError as e:
Expand Down
6 changes: 4 additions & 2 deletions burr/integrations/persisters/b_aiosqlite.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ async def is_initialized(self) -> bool:
return self._initialized

async def list_app_ids(self, partition_key: Optional[str] = None, **kwargs) -> list[str]:
"""Lists the app_ids for a given partition_key, each once, most recently saved first."""
partition_key = (
partition_key
if partition_key is not None
Expand All @@ -196,9 +197,10 @@ async def list_app_ids(self, partition_key: Optional[str] = None, **kwargs) -> l

cursor = await self.connection.cursor()
await cursor.execute(
f"SELECT DISTINCT app_id FROM {self.table_name} "
f"SELECT app_id FROM {self.table_name} "
f"WHERE partition_key = ? "
f"ORDER BY created_at DESC",
f"GROUP BY app_id "
f"ORDER BY MAX(created_at) DESC",
(partition_key,),
)
app_ids = [row[0] for row in await cursor.fetchall()]
Expand Down
7 changes: 4 additions & 3 deletions burr/integrations/persisters/b_asyncpg.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,13 +285,14 @@ async def is_initialized(self) -> bool:
await self._release_connection(conn, acquired)

async def list_app_ids(self, partition_key: str, **kwargs) -> list[str]:
"""Lists the app_ids for a given partition_key."""
"""Lists the app_ids for a given partition_key, each once, most recently saved first."""
conn, acquired = await self._get_connection()
try:
query = (
f"SELECT DISTINCT app_id, created_at FROM {self.table_name} "
f"SELECT app_id FROM {self.table_name} "
"WHERE partition_key = $1 "
"ORDER BY created_at DESC"
"GROUP BY app_id "
"ORDER BY MAX(created_at) DESC"
)
fetched_data = await conn.fetch(query, partition_key)
app_ids = [row[0] for row in fetched_data]
Expand Down
7 changes: 4 additions & 3 deletions burr/integrations/persisters/b_psycopg2.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,13 @@ def is_initialized(self) -> bool:
return self._initialized

def list_app_ids(self, partition_key: str, **kwargs) -> list[str]:
"""Lists the app_ids for a given partition_key."""
"""Lists the app_ids for a given partition_key, each once, most recently saved first."""
cursor = self.connection.cursor()
cursor.execute(
f"SELECT DISTINCT app_id, created_at FROM {self.table_name} "
f"SELECT app_id FROM {self.table_name} "
"WHERE partition_key = %s "
"ORDER BY created_at DESC",
"GROUP BY app_id "
"ORDER BY MAX(created_at) DESC",
(partition_key,),
)
app_ids = [row[0] for row in cursor.fetchall()]
Expand Down
19 changes: 19 additions & 0 deletions tests/core/test_persistence.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,25 @@ def test_persistence_lists_app_ids(persistence):
assert set(app_ids) == set(["app_id1", "app_id2"])


def test_sqlite_persister_list_app_ids_is_unique_and_newest_first():
"""Each app appears once, ordered by its most recent save."""
persister = SQLLitePersister(db_path=":memory:", table_name="test_table")
persister.initialize()
saves = [
("a-app", 0, "2026-01-01 00:00:01"),
("z-app", 0, "2026-01-01 00:00:02"),
("a-app", 1, "2026-01-01 00:00:03"),
]
for app_id, sequence_id, created_at in saves:
persister.save("pk", app_id, sequence_id, "position", State({}), "completed")
persister.connection.execute(
"UPDATE test_table SET created_at = ? WHERE app_id = ? AND sequence_id = ?",
(created_at, app_id, sequence_id),
)
persister.connection.commit()
assert persister.list_app_ids("pk") == ["a-app", "z-app"]


def test_persistence_is_initialized_false(initializing_persistence):
assert not initializing_persistence.is_initialized()

Expand Down
18 changes: 18 additions & 0 deletions tests/integrations/persisters/test_b_aiosqlite.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,24 @@ async def test_async_persistence_lists_app_ids(async_persistence):
assert set(app_ids) == set(["app_id1", "app_id2"])


async def test_async_persistence_list_app_ids_is_unique_and_newest_first(async_persistence):
"""Each app appears once, ordered by its most recent save."""
await async_persistence.initialize()
saves = [
("a-app", 0, "2026-01-01 00:00:01"),
("z-app", 0, "2026-01-01 00:00:02"),
("a-app", 1, "2026-01-01 00:00:03"),
]
for app_id, sequence_id, created_at in saves:
await async_persistence.save("pk", app_id, sequence_id, "position", State({}), "completed")
await async_persistence.connection.execute(
"UPDATE test_table SET created_at = ? WHERE app_id = ? AND sequence_id = ?",
(created_at, app_id, sequence_id),
)
await async_persistence.connection.commit()
assert await async_persistence.list_app_ids("pk") == ["a-app", "z-app"]


@pytest.mark.parametrize(
"method_name,kwargs",
[
Expand Down
42 changes: 42 additions & 0 deletions tests/integrations/persisters/test_postgresql.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# specific language governing permissions and limitations
# under the License.

import datetime
import os
import pickle

Expand Down Expand Up @@ -57,6 +58,26 @@ def test_list_app_ids(postgresql_persister):
assert "app_id2" in app_ids


def test_list_app_ids_is_unique_and_newest_first(postgresql_persister):
saves = [
("a-app", 0, "2026-01-01 00:00:01"),
("z-app", 0, "2026-01-01 00:00:02"),
("a-app", 1, "2026-01-01 00:00:03"),
]
cursor = postgresql_persister.connection.cursor()
for app_id, sequence_id, created_at in saves:
postgresql_persister.save(
"list-order", app_id, sequence_id, "pos", state.State({}), "completed"
)
cursor.execute(
"UPDATE testtable SET created_at = %s "
"WHERE partition_key = %s AND app_id = %s AND sequence_id = %s",
(created_at, "list-order", app_id, sequence_id),
)
postgresql_persister.connection.commit()
assert postgresql_persister.list_app_ids("list-order") == ["a-app", "z-app"]


def test_load_nonexistent_key(postgresql_persister):
state_data = postgresql_persister.load("pk", "nonexistent_key")
assert state_data is None
Expand Down Expand Up @@ -158,6 +179,27 @@ async def test_async_list_app_ids(asyncpostgresql_persister):
assert "app_id2" in app_ids


async def test_async_list_app_ids_is_unique_and_newest_first(asyncpostgresql_persister):
saves = [
("a-app", 0, "2026-01-01 00:00:01"),
("z-app", 0, "2026-01-01 00:00:02"),
("a-app", 1, "2026-01-01 00:00:03"),
]
for app_id, sequence_id, created_at in saves:
await asyncpostgresql_persister.save(
"list-order", app_id, sequence_id, "pos", state.State({}), "completed"
)
await asyncpostgresql_persister.connection.execute(
"UPDATE testtable_async SET created_at = $1 "
"WHERE partition_key = $2 AND app_id = $3 AND sequence_id = $4",
datetime.datetime.fromisoformat(created_at),
"list-order",
app_id,
sequence_id,
)
assert await asyncpostgresql_persister.list_app_ids("list-order") == ["a-app", "z-app"]


async def test_async_load_nonexistent_key(asyncpostgresql_persister):
state_data = await asyncpostgresql_persister.load("pk", "nonexistent_key")
assert state_data is None
Loading