Skip to content

Commit bf5724a

Browse files
author
Nathan Gillett
committed
fix(sdk): start export thread before releasing enqueue lock
Prevents flush() from joining a thread that is still in _pending but not yet started. Signed-off-by: Nathan Gillett <nathan@intentproof.io>
1 parent 23eefa8 commit bf5724a

2 files changed

Lines changed: 34 additions & 1 deletion

File tree

src/intentproof/http_exporter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def enqueue(self, event: Mapping[str, Any]) -> None:
4848
)
4949
with self._lock:
5050
self._pending.append(thread)
51-
thread.start()
51+
thread.start()
5252

5353
def _export_one(self, event: dict[str, Any]) -> None:
5454
try:

tests/test_exporter.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import os
2+
import threading
23

34
from intentproof.exporter import ingest_request_headers
5+
from intentproof.http_exporter import HttpExporter
46

57

68
def test_ingest_request_headers_includes_bearer_token() -> None:
@@ -16,6 +18,37 @@ def test_ingest_request_headers_includes_bearer_token() -> None:
1618
os.environ["INTENTPROOF_INGEST_TOKEN"] = previous
1719

1820

21+
def test_enqueue_starts_thread_before_releasing_lock() -> None:
22+
order: list[str] = []
23+
inner = threading.Lock()
24+
25+
class TrackingLock:
26+
def __enter__(self) -> "TrackingLock":
27+
inner.acquire()
28+
return self
29+
30+
def __exit__(self, *args: object) -> None:
31+
order.append("lock_release")
32+
inner.release()
33+
34+
exporter = HttpExporter("http://127.0.0.1:9787/v1/events")
35+
exporter._lock = TrackingLock() # type: ignore[assignment]
36+
37+
original_start = threading.Thread.start
38+
39+
def tracked_start(self: threading.Thread) -> None:
40+
order.append("start")
41+
original_start(self)
42+
43+
threading.Thread.start = tracked_start # type: ignore[method-assign]
44+
try:
45+
exporter.enqueue({"schema": "intentproof.event.v1"})
46+
finally:
47+
threading.Thread.start = original_start # type: ignore[method-assign]
48+
49+
assert order.index("start") < order.index("lock_release")
50+
51+
1952
def test_ingest_request_headers_omits_authorization_without_token() -> None:
2053
previous = os.environ.get("INTENTPROOF_INGEST_TOKEN")
2154
os.environ.pop("INTENTPROOF_INGEST_TOKEN", None)

0 commit comments

Comments
 (0)