# Brama
Brama is an Elixir library for reliable connection management with external dependencies. It provides robust tracking of connection statuses to APIs, services, databases, or any external system, with built-in circuit breaking to prevent cascading failures.
> This library is my first attempt at utilizing the ["stdlib" approach](https://ghuntley.com/stdlib/) for building software. This method involves creating comprehensive specifications before implementation, building a collection of Cursor rules to guide development, and treating AI as an autonomous agent rather than just a coding assistant. The whole library is going to be written by Cursor Agent with my supervision.
## Overview
When your application depends on external systems, knowing their availability status becomes critical. Brama serves as a gatekeeper (the name "Brama" means "gate" in several languages), monitoring your connections and protecting your application from external system failures.
## Features
- **Connection Monitoring**: Track status of any connection
- **Circuit Breaking**: Automatically prevent requests to failing systems after a threshold is reached
- **Self-Healing**: Connections automatically reset after a configurable expiry time
- **Status Notifications**: Subscribe to connection status change events
- **Failure Isolation**: Protect your application from cascading failures
- **Minimal Configuration**: Simple setup with reasonable defaults
- **Decorator API**: Simple function decorators for automatic circuit breaking
- **Flexible Expiry Strategies**: Fixed or progressive backoff strategies
## Installation
Add Brama to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:brama, "~> 0.1.0"},
]
end
```
## Circuit Breaking Mechanism
Brama implements circuit breaking for any connection with these behaviors:
1. Each connection type is tracked separately
2. After a configurable number of failed attempts (default: 10), the circuit opens
3. When open, all requests are rejected without attempting the external call
4. After a configurable time period (default: 1 minute), the circuit transitions to half-open
5. Connections have immediate status updates
## Configuration Options
```elixir
config :brama,
max_attempts: 10, # Attempts before circuit opens
cleanup_interval: 10_000, # Status check interval in ms
expiry: 60_000 # Circuit open duration in ms
```
## Basic Usage
```elixir
# Register a connection
Brama.register("payment_api", type: :http, scope: "payment_services")
# Before making an external call
if Brama.available?("payment_api") do
case make_api_call() do
{:ok, result} ->
Brama.success("payment_api")
{:ok, result}
{:error, reason} ->
Brama.failure("payment_api")
{:error, reason}
end
else
# Use fallback or return error
{:error, :service_unavailable}
end
```
## Decorator API
For a cleaner integration, use the decorator API:
```elixir
defmodule PaymentService do
use Brama.Decorator
@decorate circuit_breaker(identifier: "payment_api")
def process_payment(payment) do
PaymentAPI.process(payment)
end
@decorate circuit_breaker(
identifier: "refund_api",
error_handler: fn
{:ok, _} -> :success
{:error, :invalid_amount} -> {:failure, :validation_error}
{:error, :network_timeout} -> {:failure, :service_unavailable}
_ -> :ignore
end
)
def process_refund(refund) do
RefundAPI.process(refund)
end
end
```
## Expiry Strategies
Brama supports different strategies for determining how long a circuit remains open:
### Fixed Expiry (Default)
```elixir
Brama.configure("payment_api",
expiry_strategy: :fixed, # This is the default strategy
expiry: 30_000 # 30 seconds
)
```
### Progressive Backoff
```elixir
Brama.configure("flaky_service",
expiry_strategy: :progressive,
initial_expiry: 5_000, # Start with 5 seconds
max_expiry: 300_000, # Cap at 5 minutes
backoff_factor: 2.0 # Double the time with each failure
)
```
With progressive backoff, expiry times would increase with each failure: 5s → 10s → 20s → 40s → etc.
## Event Notifications
Subscribe to connection status changes:
```elixir
# Subscribe to all state changes
Brama.subscribe(events: [:state_change])
# Handle events in a GenServer
def handle_info({:brama_event, event}, state) do
Logger.info("Connection #{event.connection} changed to #{event.data.new_state}")
{:noreply, state}
end
```
## Advanced Usage
You can extend Brama for specific needs:
- Create custom monitoring modules for specialized protocols
- Implement advanced health checking logic
- Build metrics collection around connection statuses
- Integrate with monitoring and alerting systems
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
Brama is released under the MIT License.