diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c04b141..eef4186 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [ main, develop ] + branches: [ main, devel ] pull_request: - branches: [ main, develop ] + branches: [ main, devel ] jobs: test: @@ -62,10 +62,10 @@ jobs: uv run coverage xml -o coverage.xml uv run coverage json -o coverage.json - name: Build PR coverage comment - if: github.event_name == 'pull_request' && (github.base_ref == 'main' || github.base_ref == 'develop') + if: github.event_name == 'pull_request' && (github.base_ref == 'main' || github.base_ref == 'devel') run: python scripts/build_coverage_comment.py - name: Post sticky PR coverage comment - if: github.event_name == 'pull_request' && (github.base_ref == 'main' || github.base_ref == 'develop') + if: github.event_name == 'pull_request' && (github.base_ref == 'main' || github.base_ref == 'devel') uses: marocchino/sticky-pull-request-comment@v2 with: header: coverage-report diff --git a/.github/workflows/compat.yml b/.github/workflows/compat.yml index 695a700..30cb542 100644 --- a/.github/workflows/compat.yml +++ b/.github/workflows/compat.yml @@ -2,9 +2,9 @@ name: Compatibility on: push: - branches: [ main, develop ] + branches: [ main, devel ] pull_request: - branches: [ main, develop ] + branches: [ main, devel ] types: [opened, synchronize, reopened, labeled] schedule: - cron: "0 3 * * *" diff --git a/README.md b/README.md index 150baec..77d4fdb 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ On-device ML inference monitoring for Python. Tracks latency, errors, and model metadata without any code modifications. -> **Pre-release:** The API is unstable and may change between versions. +> **Pre-release:** The API is unstable and may change between versions. Semantic versioning will apply from the first stable release. ## Install diff --git a/tests/test_platform_linux.py b/tests/test_platform_linux.py index 973f7d6..5c7d431 100644 --- a/tests/test_platform_linux.py +++ b/tests/test_platform_linux.py @@ -192,8 +192,6 @@ def test_real_cpu_freq(): assert 0 < cur <= 10_000 if max_f is not None: assert 0 < max_f <= 10_000 - if cur is not None and max_f is not None: - assert cur <= max_f @pytest.mark.requires_linux diff --git a/tests/test_queue.py b/tests/test_queue.py index 9970e41..7961fe8 100644 --- a/tests/test_queue.py +++ b/tests/test_queue.py @@ -126,6 +126,30 @@ def test_persistent_queue_rehydrates_from_disk(self, tmp_path): assert q2.length() == 2 assert q2.peek() == make_event(1) + def test_persistent_queue_rehydrates_order_when_timestamps_collide( + self, tmp_path, monkeypatch + ): + fixed_ns = 1_000_000_000_000_000_000 + monkeypatch.setattr("wildedge.queue.time.time_ns", lambda: fixed_ns) + + q1 = EventQueue( + max_size=10, + policy=QueuePolicy.OPPORTUNISTIC, + persist_to_disk=True, + disk_dir=str(tmp_path), + ) + for i in range(5): + q1.add(make_event(i)) + + q2 = EventQueue( + max_size=10, + policy=QueuePolicy.OPPORTUNISTIC, + persist_to_disk=True, + disk_dir=str(tmp_path), + ) + assert q2.length() == 5 + assert [e["event_id"] for e in q2.peek_many(5)] == ["0", "1", "2", "3", "4"] + def test_persistent_queue_remove_deletes_files(self, tmp_path): q = EventQueue( max_size=10, diff --git a/wildedge/queue.py b/wildedge/queue.py index 74c27f9..44c6c50 100644 --- a/wildedge/queue.py +++ b/wildedge/queue.py @@ -35,6 +35,7 @@ def __init__( self.disk_dir = Path(disk_dir).expanduser() if disk_dir else None self._event_paths: deque[Path] = deque() self.lock = threading.Lock() + self._persist_seq = 0 if self.persist_to_disk: if self.disk_dir is None: raise ValueError("disk_dir is required when persist_to_disk=True") @@ -63,7 +64,8 @@ def persist_event(self, event: dict) -> None: if not self.persist_to_disk: return assert self.disk_dir is not None - filename = f"{time.time_ns()}-{uuid.uuid4()}.json" + filename = f"{time.time_ns():020d}-{self._persist_seq:010d}-{uuid.uuid4()}.json" + self._persist_seq += 1 path = self.disk_dir / filename path.write_text(json.dumps(event, separators=(",", ":"), ensure_ascii=True)) self._event_paths.append(path)