5a558eb09e
TypeScript SDK Compatibility V1.x E2E Tests / Select Node version matrix (push) Has been cancelled
TypeScript SDK Compatibility V1.x E2E Tests / TypeScript SDK Compatibility V1.x E2E Tests Node ${{matrix.node_version}} (push) Has been cancelled
TypeScript SDK E2E Tests / TypeScript SDK E2E Tests Node ${{matrix.node_version}} (push) Has been cancelled
Opik Optimizer - E2E Tests / build-opik (push) Has been cancelled
TypeScript SDK Compatibility V1.x E2E Tests / build-opik (push) Has been cancelled
Python SDK E2E Tests / Select Python version matrix (push) Has been cancelled
Python SDK E2E Tests / Python SDK E2E Tests ${{matrix.python_version}} (push) Has been cancelled
Python SDK E2E Tests / build-opik (push) Has been cancelled
Python SDK Compatibility V1.x E2E Tests / Select Python version matrix (push) Has been cancelled
Python SDK Compatibility V1.x E2E Tests / Python SDK Compatibility V1.x E2E Tests ${{matrix.python_version}} (push) Has been cancelled
Python SDK Compatibility V1.x E2E Tests / build-opik (push) Has been cancelled
TypeScript SDK E2E Tests / Select Node version matrix (push) Has been cancelled
TypeScript SDK E2E Tests / build-opik (push) Has been cancelled
Opik Optimizer - E2E Tests / Opik Optimizer E2E Tests Python ${{matrix.python_version}} (push) Has been cancelled
Opik Optimizer - E2E Tests / Opik Optimizer Integration Smoke Tests (push) Has been cancelled
🐙 Code Quality / detect (push) Has been cancelled
🐙 Code Quality / lint (${{ matrix.leg.name }}) (push) Has been cancelled
🐙 Code Quality / summary (push) Has been cancelled
TypeScript SDK Library Integration Tests / Check Secrets (push) Has been cancelled
TypeScript SDK Library Integration Tests / opik-vercel (Vercel AI SDK / eve) (push) Has been cancelled
SDK Library Integration Tests Runner / Check Secrets (push) Has been cancelled
SDK Library Integration Tests Runner / Missed OpenAI API Key Warning (push) Has been cancelled
SDK Library Integration Tests Runner / Build (push) Has been cancelled
SDK Library Integration Tests Runner / openai_tests (push) Has been cancelled
SDK Library Integration Tests Runner / langchain_tests (push) Has been cancelled
SDK Library Integration Tests Runner / langchain_legacy_tests (push) Has been cancelled
SDK Library Integration Tests Runner / llama_index_tests (push) Has been cancelled
SDK Library Integration Tests Runner / anthropic_tests (push) Has been cancelled
SDK Library Integration Tests Runner / mistral_tests (push) Has been cancelled
SDK Library Integration Tests Runner / groq_tests (push) Has been cancelled
SDK Library Integration Tests Runner / aisuite_tests (push) Has been cancelled
SDK Library Integration Tests Runner / haystack_tests (push) Has been cancelled
SDK Library Integration Tests Runner / dspy_tests (push) Has been cancelled
SDK Library Integration Tests Runner / crewai_v0_tests (push) Has been cancelled
SDK Library Integration Tests Runner / crewai_v1_tests (push) Has been cancelled
SDK Library Integration Tests Runner / genai_tests (push) Has been cancelled
SDK Library Integration Tests Runner / adk_tests (push) Has been cancelled
SDK Library Integration Tests Runner / adk_legacy_1_3_0_tests (push) Has been cancelled
SDK Library Integration Tests Runner / evaluation_metrics_tests (push) Has been cancelled
SDK Library Integration Tests Runner / bedrock_tests (push) Has been cancelled
SDK Library Integration Tests Runner / litellm_tests (push) Has been cancelled
SDK Library Integration Tests Runner / harbor_tests (push) Has been cancelled
SDK Library Integration Tests Runner / Slack Notification (push) Has been cancelled
Lint Opik Helm Chart / render-equality (push) Has been cancelled
Opik Optimizer - Unit Tests / Opik Optimizer Unit Tests Python ${{matrix.python_version}} (push) Has been cancelled
Python BE E2E Tests / Python BE E2E (push) Has been cancelled
Python Backend Tests / run-python-backend-tests (push) Has been cancelled
Python SDK Unit Tests / Python SDK Unit Tests ${{matrix.python_version}} (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
SDK E2E Libraries Integration Tests / Check Secrets (push) Has been cancelled
SDK E2E Libraries Integration Tests / Missed OpenAI API Key Warning (push) Has been cancelled
SDK E2E Libraries Integration Tests / build-opik (push) Has been cancelled
SDK E2E Libraries Integration Tests / E2E Lib Integration Python ${{matrix.python_version}} (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-gemini) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-langchain) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-openai) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-otel) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-vercel) (push) Has been cancelled
TypeScript SDK Build & Publish / build-and-publish (push) Has been cancelled
TypeScript SDK Unit Tests / Test on Node ${{ matrix.node-version }} (push) Has been cancelled
Backend Tests / discover-tests (push) Has been cancelled
Backend Tests / ${{ matrix.name }} (push) Has been cancelled
Build and Publish SDK / build-and-publish (push) Has been cancelled
Build Opik Docker Images / set-version (push) Has been cancelled
Build Opik Docker Images / build-backend (push) Has been cancelled
Build Opik Docker Images / build-sandbox-executor-python (push) Has been cancelled
Build Opik Docker Images / build-python-backend (push) Has been cancelled
Build Opik Docker Images / build-frontend (push) Has been cancelled
Build Opik Docker Images / create-git-tag (push) Has been cancelled
ClickHouse Migration Cluster Check / validate-clickhouse-migrations (push) Has been cancelled
Docs - Publish / run (push) Has been cancelled
E2E Tests - Post Merge (v2) / 🧪 E2E v2 Tests (${{ github.event.inputs.tier || 't1' }}) (push) Has been cancelled
E2E Tests - Post Merge (v2) / 📢 Slack Notification (push) Has been cancelled
Frontend Unit Tests / Test on Node 20 (push) Has been cancelled
Guardrails E2E Tests / Select Python version matrix (push) Has been cancelled
Guardrails E2E Tests / Guardrails E2E Tests ${{matrix.python_version}} (push) Has been cancelled
Guardrails E2E Tests / 📢 Slack Notification (push) Has been cancelled
Guardrails Backend Unit Tests / Guardrails Backend Unit Tests (push) Has been cancelled
Guardrails Backend Unit Tests / 📢 Slack Notification (push) Has been cancelled
Lint Opik Helm Chart / lint-helm-chart (Helm v3.21.0) (push) Has been cancelled
Lint Opik Helm Chart / lint-helm-chart (Helm v4.2.0) (push) Has been cancelled
Lint Opik Helm Chart / unittest-helm-chart (push) Has been cancelled
214 lines
6.7 KiB
Python
214 lines
6.7 KiB
Python
import threading
|
|
import time
|
|
|
|
import pytest
|
|
|
|
from opik.message_processing import message_queue
|
|
|
|
|
|
def test_message_queue_accept_put_without_discarding___max_size_handling():
|
|
max_size = 10
|
|
queue: message_queue.MessageQueue[str] = message_queue.MessageQueue(max_size)
|
|
|
|
queue.put("the oldest message")
|
|
assert queue.accept_put_without_discarding() is True
|
|
|
|
for i in range(max_size - 1):
|
|
assert queue.accept_put_without_discarding() is True, f"Failed at {i}"
|
|
queue.put(f"line: {i}")
|
|
|
|
assert queue.accept_put_without_discarding() is False
|
|
|
|
# put one more message to have the oldest discarded
|
|
queue.put("the newest message")
|
|
|
|
# check that the oldest message ("the oldest message") was discarded
|
|
assert queue.get(0.0001) == "line: 0"
|
|
|
|
|
|
def test_message_queue_accept_put_without_discarding___max_size_none():
|
|
queue: message_queue.MessageQueue[str] = message_queue.MessageQueue(None)
|
|
for i in range(100):
|
|
queue.put(f"line: {i}")
|
|
|
|
assert queue.accept_put_without_discarding() is True
|
|
|
|
|
|
def test_message_queue_task_done__fresh_queue__reports_all_tasks_done():
|
|
queue: message_queue.MessageQueue[str] = message_queue.MessageQueue()
|
|
assert queue.all_tasks_done() is True
|
|
|
|
|
|
def test_message_queue_task_done__put_then_task_done__quiescence_restored():
|
|
queue: message_queue.MessageQueue[str] = message_queue.MessageQueue()
|
|
|
|
queue.put("msg-1")
|
|
assert queue.all_tasks_done() is False
|
|
|
|
queue.get(0.0001)
|
|
# Popping does not mark the task done — it's in flight until task_done().
|
|
assert queue.all_tasks_done() is False
|
|
|
|
queue.task_done()
|
|
assert queue.all_tasks_done() is True
|
|
|
|
|
|
def test_message_queue_task_done__multiple_puts__decremented_one_by_one():
|
|
queue: message_queue.MessageQueue[str] = message_queue.MessageQueue()
|
|
|
|
for i in range(5):
|
|
queue.put(f"msg-{i}")
|
|
|
|
assert queue.all_tasks_done() is False
|
|
|
|
for _ in range(4):
|
|
queue.get(0.0001)
|
|
queue.task_done()
|
|
assert queue.all_tasks_done() is False
|
|
|
|
queue.get(0.0001)
|
|
queue.task_done()
|
|
assert queue.all_tasks_done() is True
|
|
|
|
|
|
def test_message_queue_task_done__put_back_does_not_increment_counter():
|
|
queue: message_queue.MessageQueue[str] = message_queue.MessageQueue()
|
|
|
|
queue.put("msg")
|
|
msg = queue.get(0.0001)
|
|
# Re-enqueue (delivery-time deferral / rate-limit retry): the task is
|
|
# still in flight, so the counter must not change.
|
|
queue.put_back(msg)
|
|
assert queue.all_tasks_done() is False
|
|
|
|
queue.get(0.0001)
|
|
queue.task_done()
|
|
assert queue.all_tasks_done() is True
|
|
|
|
|
|
def test_message_queue_task_done__called_too_many_times__raises():
|
|
queue: message_queue.MessageQueue[str] = message_queue.MessageQueue()
|
|
|
|
queue.put("msg")
|
|
queue.get(0.0001)
|
|
queue.task_done()
|
|
|
|
with pytest.raises(ValueError):
|
|
queue.task_done()
|
|
|
|
|
|
def test_message_queue_task_done__maxlen_eviction__does_not_increment_counter():
|
|
# When a bounded queue is full, appendleft silently evicts the oldest
|
|
# item. The displaced message's task carries over to the newcomer, so
|
|
# the unfinished-task count must not grow.
|
|
queue: message_queue.MessageQueue[str] = message_queue.MessageQueue(max_length=2)
|
|
|
|
queue.put("a")
|
|
queue.put("b")
|
|
# Now full — this put evicts the oldest end.
|
|
queue.put("c")
|
|
|
|
# Only two messages are reachable; only two task_done() calls should
|
|
# bring the queue back to quiescence.
|
|
queue.get(0.0001)
|
|
queue.task_done()
|
|
queue.get(0.0001)
|
|
queue.task_done()
|
|
assert queue.all_tasks_done() is True
|
|
|
|
|
|
def test_message_queue_put_back__bounded_queue_full__evicted_task_is_released():
|
|
# Regression: on a bounded queue, `put_back` (used by the rate-limit
|
|
# retry path) silently evicts an item from the opposite end. That
|
|
# evicted item was already counted in `_unfinished_tasks` at its
|
|
# original `put()` time and will never see `task_done()` — so the
|
|
# counter must drop by 1 to avoid stalling `join()`.
|
|
queue: message_queue.MessageQueue[str] = message_queue.MessageQueue(max_length=2)
|
|
|
|
queue.put("a") # unfinished=1
|
|
queue.put("b") # unfinished=2; deque [b, a]
|
|
|
|
# Simulate a consumer popping a message and being rate-limited mid-process.
|
|
msg = queue.get(0.0001) # deque [b]; unfinished=2 (msg still in flight)
|
|
assert msg == "a"
|
|
|
|
queue.put("c") # unfinished=3; deque [c, b]
|
|
|
|
# Re-enqueue the popped message: the deque is full, so `append` evicts
|
|
# "c" from the left. "c" will never be processed, so its task slot must
|
|
# be released.
|
|
queue.put_back(msg)
|
|
|
|
# Two reachable messages — two task_done() calls must restore quiescence.
|
|
queue.get(0.0001)
|
|
queue.task_done()
|
|
queue.get(0.0001)
|
|
queue.task_done()
|
|
assert queue.all_tasks_done() is True
|
|
assert queue.join(timeout=0.05) is True
|
|
|
|
|
|
def test_message_queue_clear__resets_counter_and_unblocks_join():
|
|
queue: message_queue.MessageQueue[str] = message_queue.MessageQueue()
|
|
|
|
for i in range(3):
|
|
queue.put(f"msg-{i}")
|
|
|
|
assert queue.all_tasks_done() is False
|
|
|
|
queue.clear()
|
|
|
|
# clear() drops messages that will never see task_done(); the counter
|
|
# must be reset, otherwise join()/all_tasks_done() would stall forever.
|
|
assert queue.all_tasks_done() is True
|
|
assert queue.join(timeout=0.05) is True
|
|
|
|
|
|
def test_message_queue_join__no_pending_tasks__returns_true_immediately():
|
|
queue: message_queue.MessageQueue[str] = message_queue.MessageQueue()
|
|
start = time.monotonic()
|
|
assert queue.join(timeout=1.0) is True
|
|
assert time.monotonic() - start < 0.1
|
|
|
|
|
|
def test_message_queue_join__pending_tasks_with_timeout__returns_false():
|
|
queue: message_queue.MessageQueue[str] = message_queue.MessageQueue()
|
|
queue.put("msg")
|
|
|
|
start = time.monotonic()
|
|
assert queue.join(timeout=0.1) is False
|
|
elapsed = time.monotonic() - start
|
|
assert 0.1 <= elapsed < 0.5
|
|
|
|
|
|
def test_message_queue_join__task_done_in_other_thread__wakes_waiter():
|
|
queue: message_queue.MessageQueue[str] = message_queue.MessageQueue()
|
|
queue.put("msg")
|
|
queue.get(0.0001)
|
|
|
|
def _consumer() -> None:
|
|
time.sleep(0.05)
|
|
queue.task_done()
|
|
|
|
threading.Thread(target=_consumer, daemon=True).start()
|
|
|
|
start = time.monotonic()
|
|
assert queue.join(timeout=2.0) is True
|
|
elapsed = time.monotonic() - start
|
|
# Should wake up shortly after the 0.05s sleep, not wait the full timeout.
|
|
assert elapsed < 1.0
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"maximal_queue_size,batch_factor,expected",
|
|
[
|
|
(100, 10, 10),
|
|
(100, 0, 100),
|
|
],
|
|
)
|
|
def test_calculate_max_queue_size(maximal_queue_size, batch_factor, expected):
|
|
assert (
|
|
message_queue.calculate_max_queue_size(maximal_queue_size, batch_factor)
|
|
== expected
|
|
)
|