22
33import json
44import sqlite3
5+ import threading
56from dataclasses import dataclass
67from pathlib import Path
78
@@ -51,8 +52,9 @@ class Storage:
5152 def __init__ (self , path : Path ) -> None :
5253 self .path = path
5354 self .path .parent .mkdir (parents = True , exist_ok = True )
54- self .connection = sqlite3 .connect (self .path )
55+ self .connection = sqlite3 .connect (self .path , check_same_thread = False )
5556 self .connection .row_factory = sqlite3 .Row
57+ self ._lock = threading .Lock ()
5658 self ._init_schema ()
5759
5860 def _init_schema (self ) -> None :
@@ -372,32 +374,33 @@ def get_health_snapshot(self) -> HealthSnapshot:
372374 )
373375
374376 def save_response_draft (self , draft : ResponseDraft ) -> None :
375- self .connection .execute (
376- """
377- INSERT INTO response_drafts (
378- project_id, text, variant, demo_available, demo_summary,
379- demo_path, demo_archive_path, status, updated_at
377+ with self ._lock :
378+ self .connection .execute (
379+ """
380+ INSERT INTO response_drafts (
381+ project_id, text, variant, demo_available, demo_summary,
382+ demo_path, demo_archive_path, status, updated_at
383+ )
384+ VALUES (?, ?, ?, ?, ?, NULL, NULL, 'draft', CURRENT_TIMESTAMP)
385+ ON CONFLICT(project_id) DO UPDATE SET
386+ text = excluded.text,
387+ variant = excluded.variant,
388+ demo_available = excluded.demo_available,
389+ demo_summary = excluded.demo_summary,
390+ demo_path = NULL,
391+ demo_archive_path = NULL,
392+ status = 'draft',
393+ updated_at = CURRENT_TIMESTAMP
394+ """ ,
395+ (
396+ draft .project_id ,
397+ draft .text ,
398+ draft .variant ,
399+ 1 if draft .demo_available else 0 ,
400+ draft .demo_summary or None ,
401+ ),
380402 )
381- VALUES (?, ?, ?, ?, ?, NULL, NULL, 'draft', CURRENT_TIMESTAMP)
382- ON CONFLICT(project_id) DO UPDATE SET
383- text = excluded.text,
384- variant = excluded.variant,
385- demo_available = excluded.demo_available,
386- demo_summary = excluded.demo_summary,
387- demo_path = NULL,
388- demo_archive_path = NULL,
389- status = 'draft',
390- updated_at = CURRENT_TIMESTAMP
391- """ ,
392- (
393- draft .project_id ,
394- draft .text ,
395- draft .variant ,
396- 1 if draft .demo_available else 0 ,
397- draft .demo_summary or None ,
398- ),
399- )
400- self .connection .commit ()
403+ self .connection .commit ()
401404
402405 def get_response_draft (self , project_id : int ) -> ResponseDraft | None :
403406 row = self .connection .execute (
@@ -421,17 +424,18 @@ def get_response_draft(self, project_id: int) -> ResponseDraft | None:
421424 )
422425
423426 def save_demo_project_artifacts (self , project_id : int , demo_path : str , demo_archive_path : str ) -> None :
424- self .connection .execute (
425- """
426- UPDATE response_drafts
427- SET demo_path = ?,
428- demo_archive_path = ?,
429- updated_at = CURRENT_TIMESTAMP
430- WHERE project_id = ?
431- """ ,
432- (demo_path , demo_archive_path , project_id ),
433- )
434- self .connection .commit ()
427+ with self ._lock :
428+ self .connection .execute (
429+ """
430+ UPDATE response_drafts
431+ SET demo_path = ?,
432+ demo_archive_path = ?,
433+ updated_at = CURRENT_TIMESTAMP
434+ WHERE project_id = ?
435+ """ ,
436+ (demo_path , demo_archive_path , project_id ),
437+ )
438+ self .connection .commit ()
435439
436440 def mark_response_draft_sent_manually (self , project_id : int ) -> None :
437441 self .connection .execute (
0 commit comments