Skip to content

Commit 6be83fa

Browse files
committed
refactor: split run read queries
1 parent 0ad67ee commit 6be83fa

3 files changed

Lines changed: 142 additions & 72 deletions

File tree

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
from codealmanac.database.sqlite import SQLiteConnection, SQLiteRow
2+
from codealmanac.services.runs.models import (
3+
QueuedRun,
4+
RunRecord,
5+
RunSpec,
6+
RunStatus,
7+
)
8+
9+
10+
def list_run_records(
11+
connection: SQLiteConnection,
12+
limit: int | None,
13+
repository_id: str | None = None,
14+
) -> tuple[RunRecord, ...]:
15+
query = """
16+
SELECT record_json
17+
FROM runs
18+
"""
19+
parameters: tuple[object, ...]
20+
if repository_id is None:
21+
parameters = ()
22+
else:
23+
query = f"{query} WHERE repository_id = ?"
24+
parameters = (repository_id,)
25+
query = f"""
26+
{query}
27+
ORDER BY created_at DESC, run_id DESC
28+
"""
29+
if limit is not None:
30+
query = f"{query} LIMIT ?"
31+
parameters = (*parameters, limit)
32+
rows = connection.execute(query, parameters).fetchall()
33+
return tuple(run_record_from_row(row) for row in rows)
34+
35+
36+
def read_run_record(
37+
connection: SQLiteConnection,
38+
run_id: str,
39+
) -> RunRecord | None:
40+
row = connection.execute(
41+
"SELECT record_json FROM runs WHERE run_id = ?",
42+
(run_id,),
43+
).fetchone()
44+
if row is None:
45+
return None
46+
return run_record_from_row(row)
47+
48+
49+
def read_run_spec(
50+
connection: SQLiteConnection,
51+
run_id: str,
52+
) -> RunSpec | None:
53+
result = read_run_with_spec(connection, run_id)
54+
if result is None:
55+
return None
56+
return result[1]
57+
58+
59+
def read_run_with_spec(
60+
connection: SQLiteConnection,
61+
run_id: str,
62+
) -> tuple[RunRecord, RunSpec | None] | None:
63+
row = connection.execute(
64+
"SELECT record_json, spec_json FROM runs WHERE run_id = ?",
65+
(run_id,),
66+
).fetchone()
67+
if row is None:
68+
return None
69+
spec = None
70+
if row["spec_json"] is not None:
71+
spec = RunSpec.model_validate_json(row["spec_json"])
72+
return (run_record_from_row(row), spec)
73+
74+
75+
def next_queued_run(connection: SQLiteConnection) -> QueuedRun | None:
76+
row = connection.execute(
77+
"""
78+
SELECT record_json, spec_json
79+
FROM runs
80+
WHERE status = ? AND spec_json IS NOT NULL
81+
ORDER BY created_at ASC, run_id ASC
82+
LIMIT 1
83+
""",
84+
(RunStatus.QUEUED.value,),
85+
).fetchone()
86+
if row is None:
87+
return None
88+
return QueuedRun(
89+
record=run_record_from_row(row),
90+
spec=RunSpec.model_validate_json(row["spec_json"]),
91+
)
92+
93+
94+
def count_queued_runs_before(
95+
connection: SQLiteConnection,
96+
record: RunRecord,
97+
) -> int:
98+
row = connection.execute(
99+
"""
100+
SELECT COUNT(*)
101+
FROM runs
102+
WHERE status = ?
103+
AND spec_json IS NOT NULL
104+
AND (
105+
created_at < ?
106+
OR (created_at = ? AND run_id < ?)
107+
)
108+
""",
109+
(
110+
RunStatus.QUEUED.value,
111+
record.created_at.isoformat(),
112+
record.created_at.isoformat(),
113+
record.run_id,
114+
),
115+
).fetchone()
116+
return int(row[0])
117+
118+
119+
def run_record_from_row(row: SQLiteRow) -> RunRecord:
120+
return RunRecord.model_validate_json(row["record_json"])

src/codealmanac/services/runs/store.py

Lines changed: 17 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@
2222
RunSpec,
2323
RunStatus,
2424
)
25+
from codealmanac.services.runs.queries import (
26+
count_queued_runs_before,
27+
list_run_records,
28+
next_queued_run,
29+
read_run_record,
30+
read_run_with_spec,
31+
)
2532
from codealmanac.services.runs.tables import RUN_TABLES
2633
from codealmanac.services.runs.transitions import (
2734
attach_harness_transcript,
@@ -73,89 +80,31 @@ def list(
7380
limit: int | None,
7481
repository_id: str | None = None,
7582
) -> tuple[RunRecord, ...]:
76-
query = """
77-
SELECT record_json
78-
FROM runs
79-
"""
80-
parameters: tuple[object, ...]
81-
if repository_id is None:
82-
parameters = ()
83-
else:
84-
query = f"{query} WHERE repository_id = ?"
85-
parameters = (repository_id,)
86-
query = f"""
87-
{query}
88-
ORDER BY created_at DESC, run_id DESC
89-
"""
90-
if limit is not None:
91-
query = f"{query} LIMIT ?"
92-
parameters = (*parameters, limit)
9383
with self.connect() as connection:
94-
rows = connection.execute(query, parameters).fetchall()
95-
return tuple(run_record_from_json(row["record_json"]) for row in rows)
84+
return list_run_records(connection, limit, repository_id)
9685

9786
def read(self, run_id: str) -> RunRecord:
9887
with self.connect() as connection:
99-
row = connection.execute(
100-
"SELECT record_json FROM runs WHERE run_id = ?",
101-
(run_id,),
102-
).fetchone()
103-
if row is None:
88+
record = read_run_record(connection, run_id)
89+
if record is None:
10490
raise NotFoundError("run", run_id)
105-
return run_record_from_json(row["record_json"])
91+
return record
10692

10793
def read_spec(self, run_id: str) -> RunSpec | None:
10894
with self.connect() as connection:
109-
row = connection.execute(
110-
"SELECT spec_json FROM runs WHERE run_id = ?",
111-
(run_id,),
112-
).fetchone()
113-
if row is None:
95+
result = read_run_with_spec(connection, run_id)
96+
if result is None:
11497
raise NotFoundError("run", run_id)
115-
if row["spec_json"] is None:
116-
return None
117-
return RunSpec.model_validate_json(row["spec_json"])
98+
_, spec = result
99+
return spec
118100

119101
def next_queued(self) -> QueuedRun | None:
120102
with self.connect() as connection:
121-
row = connection.execute(
122-
"""
123-
SELECT record_json, spec_json
124-
FROM runs
125-
WHERE status = ? AND spec_json IS NOT NULL
126-
ORDER BY created_at ASC, run_id ASC
127-
LIMIT 1
128-
""",
129-
(RunStatus.QUEUED.value,),
130-
).fetchone()
131-
if row is None:
132-
return None
133-
return QueuedRun(
134-
record=run_record_from_json(row["record_json"]),
135-
spec=RunSpec.model_validate_json(row["spec_json"]),
136-
)
103+
return next_queued_run(connection)
137104

138105
def queued_before(self, record: RunRecord) -> int:
139106
with self.connect() as connection:
140-
row = connection.execute(
141-
"""
142-
SELECT COUNT(*)
143-
FROM runs
144-
WHERE status = ?
145-
AND spec_json IS NOT NULL
146-
AND (
147-
created_at < ?
148-
OR (created_at = ? AND run_id < ?)
149-
)
150-
""",
151-
(
152-
RunStatus.QUEUED.value,
153-
record.created_at.isoformat(),
154-
record.created_at.isoformat(),
155-
record.run_id,
156-
),
157-
).fetchone()
158-
return int(row[0])
107+
return count_queued_runs_before(connection, record)
159108

160109
def acquire_worker_lock(
161110
self,
@@ -280,7 +229,3 @@ def connect(self):
280229
connection.executescript(RUN_TABLES)
281230
connection.commit()
282231
return connection
283-
284-
285-
def run_record_from_json(value: str) -> RunRecord:
286-
return RunRecord.model_validate_json(value)

tests/test_architecture.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1817,6 +1817,7 @@ def test_run_persistence_stays_split_by_responsibility():
18171817
events_text = (runs_root / "events.py").read_text(encoding="utf-8")
18181818
factory_text = (runs_root / "factory.py").read_text(encoding="utf-8")
18191819
locks_text = (runs_root / "locks.py").read_text(encoding="utf-8")
1820+
queries_text = (runs_root / "queries.py").read_text(encoding="utf-8")
18201821
service_text = (runs_root / "service.py").read_text(encoding="utf-8")
18211822
streaming_text = (runs_root / "streaming.py").read_text(encoding="utf-8")
18221823
tables_text = (runs_root / "tables.py").read_text(encoding="utf-8")
@@ -1839,6 +1840,7 @@ def test_run_persistence_stays_split_by_responsibility():
18391840
"events.py",
18401841
"factory.py",
18411842
"locks.py",
1843+
"queries.py",
18421844
"streaming.py",
18431845
"tables.py",
18441846
"transitions.py",
@@ -1851,6 +1853,9 @@ def test_run_persistence_stays_split_by_responsibility():
18511853
assert "def new_run_record(" in factory_text
18521854
assert "uuid4" in factory_text
18531855
assert "strftime" in factory_text
1856+
assert "def list_run_records(" in queries_text
1857+
assert "def read_run_record(" in queries_text
1858+
assert "def next_queued_run(" in queries_text
18541859
assert "def start_run(" in transitions_text
18551860
assert "def finish_run(" in transitions_text
18561861
assert "def cancel_run(" in transitions_text

0 commit comments

Comments
 (0)