Skip to content

Conversation

@jwilger
Copy link
Owner

@jwilger jwilger commented Dec 31, 2025

Closes #258

Summary

Implements the run_projection(projector, &backend) free function per ADR-029 and ARCHITECTURE.md v2.7, providing a simplified API for running projections with automatic leadership coordination.

Changes

  • New run_projection function - Free function that acquires leadership via ProjectorCoordinator::try_acquire() before processing events
  • ProjectionError::LeadershipError - New error variant for coordination failures (non-blocking per ADR-028)
  • InMemoryEventStore - Now implements CheckpointStore and ProjectorCoordinator directly
  • PostgresEventStore - Now implements CheckpointStore and ProjectorCoordinator directly
  • Blanket impl for &T: CheckpointStore - Enables ergonomic reference usage
  • ProjectionRunner docs updated - Documented as low-level API, recommending run_projection for most cases

API Change

Before:

ProjectionRunner::new(projector, &event_reader)
    .with_checkpoint_store(&checkpoint_store)
    .run()

After:

run_projection(my_projector, &backend).await?;

Testing

  • All 167 tests passing
  • Mutation score: 100% for core projection functionality
  • Clippy clean

Acceptance Criteria

  • run_projection(projector, &backend) free function created
  • Backend must implement EventReader + CheckpointStore + ProjectorCoordinator
  • run_projection acquires leadership before processing events
  • Returns ProjectionError if leadership unavailable (non-blocking per ADR-028)
  • Guard held for duration of event processing
  • Integration test verifies coordination behavior
  • InMemoryEventStore implements all three traits
  • PostgresEventStore implements all three traits
  • ProjectionRunner documented as low-level API

References

ADR-029 (accepted): Projection Runner API Simplification
- Replace ProjectionRunner struct with run_projection free function
- Require backend to implement EventReader + CheckpointStore + ProjectorCoordinator
- Matches execute(command, &store) ergonomics

ARCHITECTURE.md v2.7:
- Add Projection Runner API section documenting run_projection
- Update architectural principles to mention run_projection
- Add unified backend traits to summary

Relates to #258
Add run_projection(projector, &backend) as the primary projection API with
automatic leadership coordination. Backend must implement EventReader +
CheckpointStore + ProjectorCoordinator.

Changes:
- Add run_projection() free function to eventcore/src/projection.rs
- Add ProjectionError::LeadershipError for coordination failures
- InMemoryEventStore now implements CheckpointStore and ProjectorCoordinator
- PostgresEventStore now implements CheckpointStore and ProjectorCoordinator
- Add blanket impl for &T: CheckpointStore in eventcore-types
- Document ProjectionRunner as low-level API (prefer run_projection)

Tests:
- run_projection_acquires_leadership_and_processes_events
- run_projection_returns_leadership_error_when_lock_already_held
- run_projection_holds_leadership_during_event_processing
- postgres_event_store_saves_and_loads_checkpoint
- postgres_event_store_implements_projector_coordinator
- in_memory_event_store_implements_checkpoint_store
- in_memory_event_store_implements_projector_coordinator

Closes #258
@jwilger
Copy link
Owner Author

jwilger commented Dec 31, 2025

@jwilger jwilger changed the title docs: add ADR-029 and update ARCHITECTURE.md for projection runner API feat: implement run_projection free function (ADR-029) Dec 31, 2025
Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One critical issue: CoordinationGuard::drop() uses catch_unwind incorrectly, which will not catch the block_in_place panic on single-threaded runtimes.

Address review feedback: catch_unwind does not catch panics from
block_in_place setup on single-threaded runtimes. Instead, check
runtime_flavor() to determine the appropriate unlock strategy.

- Multi-threaded runtime: use block_in_place for synchronous unlock
- Single-threaded runtime: spawn task for async unlock
Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical issue with single-threaded runtime unlock in CoordinationGuard::drop(). Previous catch_unwind issue fixed correctly, but spawned task may not execute before shutdown.

Address review feedback: Document that on single-threaded runtimes,
the spawned unlock task may not execute before process shutdown.
The advisory lock is released when the PostgreSQL session ends.

Added comprehensive documentation to CoordinationGuard explaining:
- Multi-threaded vs single-threaded runtime behavior
- PostgreSQL session-scoped lock safety net
- Connection pool timeout recommendations
Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previous coordination guard drop() issue resolved. Runtime flavor check correctly avoids block_in_place panic, and documentation clearly explains lock release behavior.

Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previous issues addressed. Comprehensive documentation explains single-threaded runtime behavior and PostgreSQL session-scoped lock safety net.

@jwilger jwilger merged commit d59b179 into main Dec 31, 2025
39 checks passed
@jwilger jwilger deleted the 258-integrate-projector-coordinator branch December 31, 2025 18:05
@jwilger jwilger mentioned this pull request Dec 30, 2025
jwilger added a commit that referenced this pull request Dec 31, 2025
## 🤖 New release

* `eventcore-macros`: 0.4.0 -> 0.5.0
* `eventcore-types`: 0.4.0 -> 0.5.0 (✓ API compatible changes)
* `eventcore-postgres`: 0.4.0 -> 0.5.0 (✓ API compatible changes)
* `eventcore`: 0.4.0 -> 0.5.0 (⚠ API breaking changes)
* `eventcore-memory`: 0.4.0 -> 0.5.0 (✓ API compatible changes)
* `eventcore-testing`: 0.4.0 -> 0.5.0 (✓ API compatible changes)

### ⚠ `eventcore` breaking changes

```text
--- failure enum_variant_added: enum variant added on exhaustive enum ---

Description:
A publicly-visible enum without #[non_exhaustive] has a new variant.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#enum-variant-new
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.45.0/src/lints/enum_variant_added.ron

Failed in:
  variant ProjectionError:LeadershipError in /tmp/.tmpKfk26o/eventcore/eventcore/src/projection.rs:532
```

<details><summary><i><b>Changelog</b></i></summary><p>

## `eventcore-macros`

<blockquote>

##
[0.3.0](eventcore-macros-v0.2.0...eventcore-macros-v0.3.0)
- 2025-12-27

### Refactoring

- *(release)* switch to workspace version inheritance for full lockstep
versioning ([#221](#221))
</blockquote>

## `eventcore-types`

<blockquote>

##
[0.5.0](eventcore-types-v0.4.0...eventcore-types-v0.5.0)
- 2025-12-31

### Features

- add ProjectorCoordinator trait and PostgreSQL advisory lock
implementation ([#259](#259))
- implement run_projection free function (ADR-029)
([#263](#263))
</blockquote>

## `eventcore-postgres`

<blockquote>

##
[0.5.0](eventcore-postgres-v0.4.0...eventcore-postgres-v0.5.0)
- 2025-12-31

### Features

- add ProjectorCoordinator trait and PostgreSQL advisory lock
implementation ([#259](#259))
- implement run_projection free function (ADR-029)
([#263](#263))
</blockquote>

## `eventcore`

<blockquote>

##
[0.5.0](eventcore-v0.4.0...eventcore-v0.5.0)
- 2025-12-31

### Documentation

- align issues and ADRs with ARCHITECTURE.md guidance
([#256](#256))

### Features

- implement run_projection free function (ADR-029)
([#263](#263))
</blockquote>

## `eventcore-memory`

<blockquote>

##
[0.5.0](eventcore-memory-v0.4.0...eventcore-memory-v0.5.0)
- 2025-12-31

### Features

- add ProjectorCoordinator trait and PostgreSQL advisory lock
implementation ([#259](#259))
- implement run_projection free function (ADR-029)
([#263](#263))
</blockquote>

## `eventcore-testing`

<blockquote>

##
[0.5.0](eventcore-testing-v0.4.0...eventcore-testing-v0.5.0)
- 2025-12-31

### Features

- add ProjectorCoordinator trait and PostgreSQL advisory lock
implementation ([#259](#259))
</blockquote>


</p></details>

---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).
@jwilger jwilger mentioned this pull request Jan 2, 2026
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.

Implement run_projection free function (ADR-029)

2 participants