Skip to content

Conversation

@prosdev
Copy link
Contributor

@prosdev prosdev commented Jan 11, 2026

Summary

Implements the core Processor and Queue infrastructure for eventkit, providing a queue-agnostic event processing pipeline with two queue implementations.

Implementation

Core Components

  • Processor: Queue-agnostic orchestration layer that wires Adapter → Sequencer → Buffer → Stores
  • EventQueue Protocol: Interface for pluggable queue backends
  • DirectQueue: Inline processing (no actual queue) - default mode
  • AsyncQueue: In-process background workers with asyncio.Queue

Queue Factory

  • Config-based queue creation via EVENTKIT_QUEUE_MODE
  • Factory pattern enables swapping queues without code changes
  • Modes: direct, async, pubsub (future)

Configuration

  • EVENTKIT_QUEUE_MODE: Queue backend selection (default: direct)
  • EVENTKIT_ASYNC_WORKERS: Number of workers for AsyncQueue (default: 4)

Tests

All 136 unit tests passing

New Tests (21 tests)

  • test_processor.py: Processor orchestration, error handling, lifecycle (6 tests)
  • test_direct.py: DirectQueue operations and lifecycle (4 tests)
  • test_async.py: AsyncQueue workers, error handling, shutdown (8 tests)
  • test_factory.py: Queue factory mode selection (3 tests)

Coverage

  • Processor: 100% (24/24 statements)
  • DirectQueue: 100% (11/11 statements)
  • AsyncQueue: 96% (45/47 statements)
  • Overall: 98% coverage (548 statements, 13 missing)

Key Features

DirectQueue

  • Simple: No external dependencies
  • Fast: No queue overhead
  • No decoupling: API and processing in same thread
  • No horizontal scaling

AsyncQueue

  • Fast API responses (enqueue is O(1))
  • Parallel processing with N workers
  • Error isolation (failed events don't crash workers)
  • Graceful shutdown (drains queue before stopping)
  • No persistence (events lost on crash)
  • No horizontal scaling (single process)

Usage

# DirectQueue (default)
EVENTKIT_QUEUE_MODE=direct
queue = create_queue(processor, settings)
await queue.enqueue(event)  # Processes immediately

# AsyncQueue (background workers)
EVENTKIT_QUEUE_MODE=async
EVENTKIT_ASYNC_WORKERS=8
queue = create_queue(processor, settings)
await queue.start()  # Starts 8 workers
await queue.enqueue(event)  # Returns immediately
await queue.stop()  # Drains queue, then stops

Architecture Decisions

  1. Queue-Agnostic Processor: Processor doesn't know about queues - queues call processor.process_event()
  2. Protocol-Based Design: EventQueue Protocol enables easy swapping of implementations
  3. Factory Pattern: Config-driven queue creation for flexibility
  4. No Exceptions: Invalid events routed to error store, not thrown

Files Changed

  • src/eventkit/processing/processor.py: Main processor orchestration
  • src/eventkit/queues/base.py: EventQueue Protocol
  • src/eventkit/queues/direct.py: DirectQueue implementation
  • src/eventkit/queues/async_queue.py: AsyncQueue implementation
  • src/eventkit/queues/factory.py: Queue factory
  • src/eventkit/config.py: Queue configuration settings
  • tests/unit/processing/test_processor.py: Processor tests
  • tests/unit/queues/test_*.py: Queue tests

Closes #6

- Split spec.md (698 lines) into 4 focused files:
  - spec.md (434 lines) - High-level overview, user stories, success criteria
  - api.md (117 lines) - Endpoints, request/response formats, examples
  - data-models.md (169 lines) - RawEvent, TypedEvent schemas, normalization
  - architecture.md (365 lines) - Components, configuration, future plans

- Added queue architecture config (EVENTKIT_QUEUE_MODE) with future modes
- Added BUFFER_MAX_SIZE to config table
- Expanded 'Future: Queue Architecture' section with implementation roadmap

Benefits:
- Easier to navigate and reference specific sections
- Clear separation of concerns (what vs how vs API)
- Can link to individual docs (e.g., share API reference)
- Prevents monolithic spec files as project grows
Updated Phase 4 (Processing Pipeline) to document queue-agnostic design:

- Renamed Processor.enqueue() → process_event() for clarity
- Added Processor.start()/stop() lifecycle methods
- Added EventQueue Protocol (base.py)
- Added DirectQueue implementation (direct.py)
- Added queue factory pattern (factory.py)
- Updated file structure to include queues/ directory

Key design decisions:
- Processor is queue-agnostic (doesn't know about queues)
- process_event() is called by queues (DirectQueue, AsyncQueue, PubSubQueue)
- EventQueue Protocol enables easy swapping via config (EVENTKIT_QUEUE_MODE)

This enables swapping queue implementations:
- DirectQueue: Inline processing (no actual queue)
- AsyncQueue: In-process workers with asyncio.Queue
- PubSubQueue: Distributed workers with GCP Pub/Sub
Implementation:
- Add EventQueue Protocol (queues/base.py) for pluggable queue backends
- Add DirectQueue (queues/direct.py) for inline processing
- Add queue factory (queues/factory.py) with config-based creation
- Add QueueMode enum to config.py (DIRECT, ASYNC, PUBSUB)
- Add Processor class (processing/processor.py) with queue-agnostic design

Tests (13 new tests, all passing):
- test_direct.py: DirectQueue lifecycle and event processing
- test_factory.py: Queue factory mode selection
- test_processor.py: Processor orchestration, error handling, lifecycle

Coverage:
- Processor: 100% (24/24 statements)
- DirectQueue: 100% (11/11 statements)
- Queue Factory: 92% (12/13 statements)
- Overall: 98% coverage (500 statements, 11 missing)

Key Design:
- Processor.process_event() is called by queues (queue-agnostic)
- Processor wires: Adapter → Sequencer → Buffer → Stores
- DirectQueue processes inline (no actual queue)
- Queue factory enables swapping via EVENTKIT_QUEUE_MODE env var

Fixes:
- Fix EventAdapter Protocol to accept RawEvent (not dict)
- Fix AdapterResult usage (.ok not .success)
- Fix ErrorStore method call (store_error not write_error)

All 128 unit tests passing ✅
Implementation:
- Add AsyncQueue (queues/async_queue.py) with asyncio.Queue
- Background workers pull events and call processor.process_event()
- Configurable worker count via EVENTKIT_ASYNC_WORKERS (default: 4)
- Graceful shutdown with queue draining
- Update factory to create AsyncQueue for ASYNC mode

Tests (8 new tests, all passing):
- test_async.py: Queue operations, workers, error handling, shutdown
- test_factory.py: Updated to test AsyncQueue creation

Coverage:
- AsyncQueue: 96% (45 statements, 2 missing)
- Overall: 98% coverage (548 statements, 13 missing)

Key Features:
- Fast API responses (enqueue is O(1), returns immediately)
- Parallel processing with N configurable workers
- Error isolation (one failed event doesn't stop workers)
- Clean shutdown (waits for queue to drain)

Usage:
  EVENTKIT_QUEUE_MODE=async EVENTKIT_ASYNC_WORKERS=8
  queue = create_queue(processor, settings)
  await queue.start()  # Starts 8 workers
  await queue.enqueue(event)  # Returns immediately
  await queue.stop()  # Drains queue, then stops

All 136 unit tests passing ✅
@prosdev prosdev merged commit ea793e4 into main Jan 11, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Main Processor

2 participants