Open madawei2699 opened 1 week ago
EventRadar is a lightweight, extensible framework for building event processing pipelines in Elixir. It provides core abstractions and minimal distribution support, allowing applications to implement their own scaling strategies.
graph TB
subgraph EventRadar ["EventRadar Core"]
direction TB
subgraph Behaviors ["Core Behaviors"]
PB[Pipeline]
CB[Collector]
PRB[Processor]
DB[Distribution]
end
subgraph Types ["Core Types"]
E[Event]
C[Config]
R[Result]
end
subgraph Runtime ["Basic Runtime"]
PS[PipelineSupervisor]
PR[PipelineRegistry]
LD[LocalDistribution]
end
end
subgraph Application ["Application Layer"]
subgraph Components ["Custom Components"]
CP[Custom Pipeline]
CC[Custom Collector]
CPR[Custom Processor]
CD[Custom Distribution]
end
subgraph AppRuntime ["App Runtime"]
AS[App Supervisor]
PF[Pipeline Factory]
end
PB --> CP
CB --> CC
PRB --> CPR
DB --> CD
AS --> PF
PF --> CP
end
classDef core fill:#f9f,stroke:#333,stroke-width:2px
classDef type fill:#bbf,stroke:#333
classDef runtime fill:#bfb,stroke:#333
classDef app fill:#fbb,stroke:#333
sequenceDiagram
participant App as Application
participant ER as EventRadar
participant D as Distribution
participant PS as PipelineSupervisor
participant P as Pipeline
participant C as Collector
participant PR as Processor
%% Pipeline Creation
rect rgb(200, 223, 255)
Note over App,PR: Pipeline Creation Flow
App->>ER: start_pipeline(config, distribution)
ER->>D: distribute_pipeline(id, config)
D->>PS: start_pipeline(config)
PS->>P: start_link(config)
P->>C: init(collector_config)
P->>PR: init(processor_config)
end
%% Event Processing
rect rgb(255, 223, 200)
Note over App,PR: Event Processing Flow
P->>C: collect()
C-->>P: {:ok, events}
loop For each event
P->>PR: process(event)
PR-->>P: {:ok, processed_event}
P->>P: handle_event(processed_event)
end
end
event_radar/
├── lib/
│ ├── event_radar/
│ │ ├── core/
│ │ │ ├── pipeline.ex # Pipeline behavior
│ │ │ ├── collector.ex # Collector behavior
│ │ │ ├── processor.ex # Processor behavior
│ │ │ └── distribution.ex # Distribution behavior
│ │ │
│ │ ├── types/
│ │ │ ├── event.ex # Event type
│ │ │ ├── config.ex # Config type
│ │ │ └── result.ex # Result type
│ │ │
│ │ ├── runtime/
│ │ │ ├── supervisor.ex # Pipeline supervisor
│ │ │ ├── registry.ex # Pipeline registry
│ │ │ └── local.ex # Local distribution implementation
│ │ │
│ │ └── utils/
│ │ ├── telemetry.ex # Telemetry integration
│ │ └── logger.ex # Logging utilities
│ │
│ └── event_radar.ex # Main module
defmodule EventRadar.Core.Pipeline do
@moduledoc """
Defines the core behavior for event processing pipelines.
"""
@callback init(config :: Config.t()) :: {:ok, state :: term()} | {:error, term()}
@callback handle_event(event :: Event.t(), state :: term()) ::
{:ok, Event.t()} | {:error, term()}
end
defmodule EventRadar.Core.Collector do
@moduledoc """
Defines the behavior for event collectors.
"""
@callback init(config :: term()) :: {:ok, state :: term()} | {:error, term()}
@callback collect(state :: term()) :: {:ok, [Event.t()]} | {:error, term()}
end
defmodule EventRadar.Core.Processor do
@moduledoc """
Defines the behavior for event processors.
"""
@callback init(config :: term()) :: {:ok, state :: term()} | {:error, term()}
@callback process(event :: Event.t(), state :: term()) ::
{:ok, Event.t()} | {:error, term()}
end
defmodule EventRadar.Core.Distribution do
@moduledoc """
Defines the behavior for pipeline distribution.
"""
@callback distribute_pipeline(pipeline_id :: term(), config :: Config.t()) ::
{:ok, pid()} | {:error, term()}
@callback handle_node_change(node :: node(), action :: :up | :down) ::
:ok | {:error, term()}
end
defmodule EventRadar.Types.Event do
@type t :: %__MODULE__{
id: term(),
source: term(),
data: term(),
metadata: map()
}
defstruct [:id, :source, :data, metadata: %{}]
end
defmodule EventRadar.Types.Config do
@type collector_config :: {module(), term()}
@type processor_config :: {module(), term()}
@type t :: %__MODULE__{
id: term(),
collector: collector_config(),
processors: [processor_config()],
metadata: map()
}
defstruct [:id, :collector, :processors, metadata: %{}]
end
defmodule EventRadar.Runtime.Supervisor do
use Supervisor
def start_link(init_arg) do
Supervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
end
def init(_init_arg) do
children = [
{Registry, keys: :unique, name: EventRadar.Runtime.Registry},
{DynamicSupervisor, name: EventRadar.Runtime.PipelineSupervisor}
]
Supervisor.init(children, strategy: :one_for_one)
end
end
defmodule EventRadar.Runtime.LocalDistribution do
@behaviour EventRadar.Core.Distribution
def distribute_pipeline(pipeline_id, config) do
DynamicSupervisor.start_child(
EventRadar.Runtime.PipelineSupervisor,
{config.module, config}
)
end
def handle_node_change(_node, _action), do: :ok
end
graph TB
subgraph Application ["Application Layer"]
direction TB
subgraph Distribution ["Custom Distribution"]
DM[Distribution Manager]
LB[Load Balancer]
PH[Pipeline Health Check]
end
subgraph Management ["Pipeline Management"]
PC[Pipeline Config Store]
PF[Pipeline Factory]
PM[Pipeline Monitor]
end
subgraph Scaling ["Auto Scaling"]
SS[Scaling Strategy]
subgraph Partitions
P1[Partition 1]
P2[Partition 2]
P3[Partition N]
end
end
DM --> LB
DM --> PH
PF --> PC
PF --> PM
SS --> Partitions
end
defmodule MyApp.Pipeline do
@behaviour EventRadar.Core.Pipeline
def init(config) do
{:ok, config}
end
def handle_event(event, state) do
# Process event
{:ok, processed_event}
end
end
# Start pipeline
config = %EventRadar.Types.Config{
id: "my_pipeline",
collector: {MyApp.Collector, %{}},
processors: [{MyApp.Processor, %{}}]
}
EventRadar.start_pipeline(config)
defmodule MyApp.ClusterDistribution do
@behaviour EventRadar.Core.Distribution
def distribute_pipeline(pipeline_id, config) do
with {:ok, partition} <- PartitionManager.assign_partition(pipeline_id),
{:ok, node} <- select_node(partition),
{:ok, pid} <- start_on_node(node, config) do
{:ok, pid}
end
end
def handle_node_change(node, :down) do
redistribute_pipelines(node)
end
end
# Pipeline configuration store
defmodule MyApp.PipelineConfig do
def store(pipeline_id, config) do
:ets.insert(@ets_table, {pipeline_id, config})
end
end
# Partition management
defmodule MyApp.PartitionManager do
def assign_partition(pipeline_id) do
partition = :erlang.phash2(pipeline_id, partition_count())
{:ok, partition}
end
end
# Health monitoring
defmodule MyApp.PipelineMonitor do
use GenServer
def init(state) do
:timer.send_interval(30_000, :check_health)
{:ok, state}
end
def handle_info(:check_health, state) do
check_all_pipelines()
{:noreply, state}
end
end
# Twitter collector
defmodule MyApp.TwitterCollector do
@behaviour EventRadar.Core.Collector
def init(config) do
# Initialize Twitter API client
{:ok, config}
end
def collect(state) do
# Collect tweets
{:ok, events}
end
end
# Tweet processor
defmodule MyApp.TweetProcessor do
@behaviour EventRadar.Core.Processor
def process(event, state) do
# Process tweet
{:ok, processed_event}
end
end
# Start monitoring
defmodule MyApp.TwitterMonitor do
def start_monitoring(user_id, keywords) do
config = %EventRadar.Types.Config{
id: "twitter_#{user_id}",
collector: {MyApp.TwitterCollector, %{
user_id: user_id,
keywords: keywords
}},
processors: [
{MyApp.TweetProcessor, %{}}
]
}
EventRadar.start_pipeline(config, MyApp.ClusterDistribution)
end
end
defmodule EventRadar.TestSupport do
def create_test_pipeline(config \\ %{}) do
default_config = %EventRadar.Types.Config{
id: "test_pipeline_#{:rand.uniform(1000)}",
collector: {TestCollector, %{}},
processors: []
}
config = Map.merge(default_config, config)
EventRadar.start_pipeline(config)
end
end
defmodule MyApp.PipelineTest do
use ExUnit.Case
test "pipeline processes events correctly" do
{:ok, pipeline} = EventRadar.TestSupport.create_test_pipeline(%{
collector: {MyCollector, %{data: test_data}},
processors: [{MyProcessor, %{}}]
})
# Test pipeline behavior
assert {:ok, _events} = EventRadar.get_processed_events(pipeline)
end
end
Minimal Core Abstractions
Distribution Support
Application Layer Freedom
Runtime Support
Type System
The key improvements from V1 include:
EventRadar V1 Design Specification
1. Overview
EventRadar is a lightweight, extensible framework for building event monitoring and processing pipelines in Elixir.
1.1 Core Features
1.2 Project Structure
2. Core Abstractions
2.1 Pipeline
2.2 Collector
2.3 Processor
3. Pipeline Management
3.1 Registry
3.2 Builder
4. Scheduling System
4.1 Task Scheduler
5. Plugin System
5.1 Plugin Manager
6. Example Usage
7. Testing Strategy