Orka is a high-performance, pluggable process registry for Erlang/OTP applications. It provides fast lookups via pluggable backends (ETS-based by default), automatic process lifecycle management, rich metadata querying, and startup coordination features. Built with a pathway to distribution-aware backends like RA, Khepri, or Riak-core.
✅ Fast registration & lookup — O(1) lookups via pluggable backends
✅ Pluggable backends — ETS (default), extensible to RA, Khepri, Riak-core
✅ Automatic cleanup — Process monitors handle crashes
✅ Rich metadata — Tags and properties for flexible querying
✅ Startup coordination — Await/subscribe for service dependencies
✅ Batch operations — Atomic multi-process registration
✅ Singleton pattern — One-key-per-process constraint
✅ Zero dependencies — Pure Erlang/OTP, no external deps
✅ Fully tested — 85 test cases, all passing
- Service registries — Find services by name, type, or category
- Connection pools — Track workers and distribute load by capacity
- Startup coordination — Wait for dependencies before proceeding
- Health monitoring — Query process status and distribution
- Resource tracking — Manage distributed resources with properties
- User sessions — Track multi-service per-user workloads
- Process discovery — Efficient lookup by key or metadata
%% Register a service
orka:register({global, service, translator}, Pid, #{
tags => [online, critical],
properties => #{capacity => 100, version => "2.1"}
}).
%% Lookup by key (checks liveness)
{ok, {Key, Pid, Metadata}} = orka:lookup_alive({global, service, translator}).
%% Query by tag
OnlineServices = orka:entries_by_tag(online).
%% Query by property
HighCapacity = orka:find_by_property(capacity, 100).
%% Await service startup
{ok, Entry} = orka:await({global, service, database}, 30000).
%% Batch register (atomic, explicit Pids)
{ok, Entries} = orka:register_batch([
{{global, portfolio, user1}, Pid1, #{tags => [portfolio, user1]}},
{{global, orders, user1}, Pid2, #{tags => [orders, user1]}}
]).Start with API.md for complete documentation, then explore:
| Document | Content |
|---|---|
| API.md | Complete API reference with examples |
| docs/usage_patterns.md | 8 fundamental patterns |
| docs/singleton_examples.md | Single-instance services |
| docs/property_examples.md | Rich metadata querying |
| docs/await_examples.md | Startup coordination |
| docs/comparison.md | Orka vs gproc vs syn |
| docs/extensions/ | Future extension ideas |
┌────────────────────────────────────────────────────────────┐
│ Orka Gen_Server │
│ (registration, monitors, notify) │
└────────────────────┬─────────────────────────────────────┘
│
┌──────────────┴──────────────┐
│ │
▼ ▼
┌──────────────────┐ ┌────────────────┐
│ orka_store │ │ State Maps │
│ (pluggable) │ │ {PidSingleton │
│ │ │ PidKeyMap │
│ ┌────────────┐ │ │ Subscribers │
│ │ Backend │ │ │ MonitorMap} │
│ │ │ │ └────────────────┘
│ │ • ETS(dflt)│ │
│ │ • RA │ │
│ │ • Khepri │ │
│ │ • custom │ │
│ └────────────┘ │
└──────────────────┘
Key features:
- Pluggable backends — Swap implementations for different guarantees
orka_store_ets— Default, local-node with separate local/global stores- Extensible for
ra,khepri,riak-core, etc. (see docs/extensions)
- Gen_server for atomic writes with monitors
- Process monitors for automatic cleanup
- Efficient indices for tags and properties
ETS backend (default):
- Lookup: ~1-2 microseconds (ETS public table)
- Registration: ~10-20 microseconds (gen_server call + monitor)
- Tag query: O(n) where n = entries with tag (usually small)
Suitable for: Service discovery, startup coordination, process tracking (<10K lookups/sec)
Not suitable for: Per-message routing in high-throughput systems (>100K msg/sec). See docs/extensions/ for distributed backends and caching patterns.
Backend flexibility: Different backends offer different trade-offs:
- ETS — Local-only, ultra-fast, no persistence
- RA/Khepri — Distributed, replicated, fault-tolerant (planned extensions)
All features are thoroughly tested:
make ct # Run Common Test suite (85/85 passing)
make clean # Clean build artifacts
make erl # Start Erlang shell with orka loadedTest coverage includes:
- Registration, unregistration, lookup
- Process cleanup on crash
- Tags and properties
- Singleton constraint
- Await/subscribe coordination
- Batch operations
- Concurrent subscribers
- Backend implementations (ETS, store abstraction)
- Property-based testing
- Scope isolation (local/global)
- Concurrency and regression tests
Tests are organized in 9 test suites:
orka_SUITE.erl— Core functionality (57 tests)orka_app_SUITE.erl— Application startup (5 tests)orka_concurrency_SUITE.erl— Concurrent operations (2 tests)orka_extra_SUITE.erl— Extended features (4 tests)orka_gpt_regression_SUITE.erl— Regression tests (3 tests)orka_issue_regression_SUITE.erl— Issue regression tests (3 tests)orka_property_SUITE.erl— Property-based tests (2 tests)orka_scope_SUITE.erl— Scope isolation tests (3 tests)orka_store_SUITE.erl— Store backend tests (7 tests)
1. Backend-Specific Reads
Lookup operations go through the configured store backend (ETS by default).
2. Automatic Cleanup
Process crashes are detected via monitors and entries are automatically removed.
3. Three Levels of Metadata
- Key structure — Hierarchical:
{Scope, Type, Name} - Tags — For categorization:
online,critical,translator - Properties — For rich attributes:
region: "us-west",capacity: 100
4. Pluggable Backends
Orka's core is backend-agnostic. The default ETS backend handles single-node registries. Extensions can provide distributed backends (RA, Khepri, etc.) for cluster-wide registries. See docs/extensions/ for planned backend options.
init([]) ->
orka:register({global, service, my_service}, self(), #{tags => [online]}),
{ok, #state{}}.{ok, Pid} = orka:register_with(
{global, service, database},
#{tags => [critical]},
{db_server, start_link, []}
).{ok, _} = orka:register_single({global, service, config}, #{tags => [critical]}).{ok, Entries} = orka:register_batch([
{{global, portfolio, user1}, Pid1, #{tags => [user1, portfolio]}},
{{global, orders, user1}, Pid2, #{tags => [user1, orders]}},
{{global, risk, user1}, Pid3, #{tags => [user1, risk]}}
]).%% Block on critical dependency
{ok, {_Key, DbPid, _}} = orka:await({global, service, database}, 30000).
%% Subscribe to optional service
orka:subscribe({global, service, cache}).%% Find services in specific region
WestServices = orka:find_by_property(region, "us-west").
%% Load balance by capacity
HighCapacity = orka:find_by_property(capacity, 150).| Feature | Orka | gproc | syn |
|---|---|---|---|
| Speed | ⚡⚡⚡ Fast | ⚡⚡ Medium | ⚡⚡ Medium |
| Local registry | ✅ | ✅ | ✅ |
| Distributed | ❌ | ❌ | ✅ |
| Tags | ✅ | ✅ | ❌ |
| Properties | ✅ | ❌ | ❌ |
| Await/subscribe | ✅ | ❌ | ❌ |
| Singleton pattern | ✅ | ❌ | ❌ |
| Batch registration | ✅ | ❌ | ❌ |
| Zero dependencies | ✅ | ✅ | ✅ |
See docs/comparison.md for detailed comparison.
register/2,3— Register a processregister_with/3— Atomically start and registerregister_single/2,3— Singleton constraintlookup/1— Fast key lookup (no liveness check)lookup_dirty/1— Lock-free ETS lookup (no liveness check)lookup_alive/1— Key lookup with liveness checklookup_all/0— Get all entriesunregister/1— Remove entry
entries_by_type/1— Find by key typeentries_by_tag/1— Find by tagfind_by_property/2,3— Find by property valuecount_by_type/1,count_by_tag/1,count_by_property/2property_stats/2— Distribution analysis
register_batch/1— Batch atomic registrationregister_batch_with/1— Start and batch register atomicallyunregister_batch/1— Remove multiple entries atomicallyregister_property/3— Add queryable propertiesupdate_metadata/2— Update metadata on entryawait/2— Block on startupsubscribe/1— Non-blocking notificationadd_tag/2,remove_tag/2— Dynamic metadata
See API.md for complete documentation.
Add to your rebar.config:
{deps, [
{orka, {git, "https://github.com/goatrllr/orka.git", {branch, "main"}}}
]}.Or manually:
git clone <repo> deps/orka
make -C deps/orkaThen in your application:
{ok, _} = application:ensure_all_started(orka).orca/
├── src/
│ ├── orka.erl # Main registry module
│ └── orka_app.erl # Application callback
├── test/
│ ├── orka_SUITE.erl # Core registry tests (main test suite)
│ ├── orka_app_SUITE.erl # Application lifecycle tests
│ ├── orka_concurrency_SUITE.erl # Concurrent operations tests
│ ├── orka_extra_SUITE.erl # Extended feature tests
│ ├── orka_property_SUITE.erl # Property-based tests
│ ├── orka_gpt_regression_SUITE.erl # GPT-identified issue regressions
│ └── orka_issue_regression_SUITE.erl # Known issue regressions
├── ebin/ # Compiled BEAM files
│
├── API.md # API reference (complete documentation)
├── README.md # This file
├── DOCMAP.md # Documentation navigation guide
├── rebar.config # Build configuration
├── Makefile # Build targets
│
└── docs/
├── README.md # Documentation guide
├── usage_patterns.md # 8 fundamental patterns
├── singleton_examples.md # Single-instance services
├── property_examples.md # Rich metadata querying
├── await_examples.md # Startup coordination
├── comparison.md # vs gproc/syn
│
└── extensions/ # Extension ideas (not yet implemented)
├── README.md # Extension overview
├── orka_syn.md # Orka + Syn hybrid architecture
├── groups_examples.md # Process groups patterns
├── partial_match_options.md # Query patterns
└── message_systems.md # Kafka/RabbitMQ clone architectures
See API.md or usage_patterns.md Pattern 1
See comparison.md
Q: What's the difference between tags and properties?
- Tags: Categories (online, critical, translator)
- Properties: Attributes (region, capacity, version)
See API.md FAQ
Q: Should I use await or subscribe?
- await: Blocking on critical dependencies
- subscribe: Non-blocking optional dependencies
See API.md FAQ
Q: Can I use Orka in a distributed system? Orka is local-node only. For multi-node, see Orka + Syn patterns.
See API.md FAQ for more.
%% Register user workspace with 5 services
create_user_workspace(UserId) ->
{ok, Entries} = orka:register_batch([
{{global, portfolio, UserId}, Pid1, #{tags => [UserId, portfolio]}},
{{global, technical, UserId}, Pid2, #{tags => [UserId, technical]}},
{{global, orders, UserId}, Pid3, #{tags => [UserId, orders]}},
{{global, risk, UserId}, Pid4, #{tags => [UserId, risk]}},
{{global, monitoring, UserId}, Pid5, #{tags => [UserId, monitoring]}}
]),
{ok, Entries}.
%% Query all services for user
get_user_services(UserId) ->
orka:entries_by_tag(UserId).
%% Broadcast to all user services
broadcast_to_user(UserId, Message) ->
Services = get_user_services(UserId),
[Pid ! {user_message, UserId, Message} || {_, Pid, _} <- Services].check_system_health() ->
OnlineServices = orka:count_by_tag(online),
CriticalServices = orka:count_by_tag(critical),
case {OnlineServices, CriticalServices} of
{_, 0} -> {error, critical_services_down};
{N, M} when N < 2 -> {warning, low_availability};
_ -> ok
end.%% In application start
start(normal, _Args) ->
application:ensure_all_started(orka),
my_sup:start_link(),
%% Wait for critical services
case wait_for_services([database, cache, queue], 30000) of
ok -> io:format("System ready~n", []);
{error, timeout} -> {error, startup_timeout}
end.make # Compile
make clean # Clean
make ct # Run tests
make erl # Shellmake ct # All tests
make erl # Interactive shell
ct:run_test("test/orka_SUITE"). # Specific suiteFollows standard Erlang conventions. See modules for examples.
Contributions welcome! Please:
- Add tests for new features
- Update documentation
- Follow existing code style
- Ensure all 71 tests pass
MIT - See LICENSE file
- Inspired by gproc and syn process registries
- ETS-based architecture for performance
- Common Test, PropEr for comprehensive testing
- Documentation: See API.md and docs/
- Examples: Each doc file includes detailed examples
- Tests: See test/orka_SUITE.erl for implementation examples
Latest Version: 1.0 | Status: Production Ready ✅ | Tests: 71/71 passing | Code: 650 lines (1,481 total with docs) | Erlang: OTP 24+ | License: MIT
