///|
pub(all) struct QueueConfig {
seed : UInt64
customers : Int
max_arrival_gap : Int
service_time : Int
}
///|
pub(all) struct QueueResult {
customers : Int
events_executed : Int
final_tick : Int
min_arrival : Int
max_arrival : Int
digest : UInt64
trace : Array[TraceEntry]
}
///|
pub fn queue_config(
seed? : UInt64 = 4096UL,
customers? : Int = 5,
max_arrival_gap? : Int = 4,
service_time? : Int = 3,
) -> QueueConfig {
{
seed,
customers: if customers < 0 {
0
} else {
customers
},
max_arrival_gap: if max_arrival_gap < 1 {
1
} else {
max_arrival_gap
},
service_time: if service_time < 1 {
1
} else {
service_time
},
}
}
///|
pub fn run_queue_model(config : QueueConfig) -> QueueResult {
let sim = Sim::new(seed=config.seed)
let mut current_tick = 0
let mut last_finish = 0
for customer in 0.. last_finish {
current_tick
} else {
last_finish
}
let finish = start + config.service_time
last_finish = finish
ignore(
sim.schedule_at(finish, "customer:" + customer.to_string() + ":finish"),
)
sim.inc_counter("customers")
sim.sample("arrival_tick", current_tick)
sim.sample("finish_tick", finish)
}
ignore(sim.run_until_idle())
let arrivals = sim.metrics().summary("arrival_tick")
{
customers: sim.metrics().counter("customers"),
events_executed: sim.metrics().counter("events_executed"),
final_tick: sim.time(),
min_arrival: arrivals.min,
max_arrival: arrivals.max,
digest: sim.digest(),
trace: sim.trace(),
}
}
///|
/// Projects queue arrivals and completions into the shared message event stream.
pub fn QueueResult::event_stream(self : QueueResult) -> EventStream {
let stream = EventStream::new()
for entry in self.trace {
ignore(
stream.record(
@core.message_event_kind(),
entry.tick,
entry.kind,
correlation_id=entry.event_id.to_string(),
source="queue",
target="worker",
payload=entry.detail,
),
)
}
stream
}
///|
pub(all) struct RetryConfig {
seed : UInt64
max_attempts : Int
fail_until : Int
initial_backoff : Int
jitter : Int
}
///|
pub(all) struct RetryResult {
attempts : Int
retries : Int
success : Bool
final_tick : Int
digest : UInt64
trace : Array[TraceEntry]
}
///|
pub fn retry_config(
seed? : UInt64 = 2026UL,
max_attempts? : Int = 4,
fail_until? : Int = 2,
initial_backoff? : Int = 1,
jitter? : Int = 1,
) -> RetryConfig {
{
seed,
max_attempts: if max_attempts < 1 {
1
} else {
max_attempts
},
fail_until: if fail_until < 0 {
0
} else {
fail_until
},
initial_backoff: if initial_backoff < 1 {
1
} else {
initial_backoff
},
jitter: if jitter < 0 {
0
} else {
jitter
},
}
}
///|
pub fn run_retry_model(config : RetryConfig) -> RetryResult {
let sim = Sim::new(seed=config.seed)
let backoff = Backoff::new(
initial=config.initial_backoff,
jitter=config.jitter,
)
let mut tick = 0
let mut success = false
for attempt in 1..<=config.max_attempts {
ignore(sim.schedule_at(tick, "attempt:" + attempt.to_string()))
sim.inc_counter("attempts")
if attempt <= config.fail_until {
ignore(
sim.schedule_at(tick, "attempt:" + attempt.to_string() + ":failed"),
)
tick += backoff.next_delay(sim.rng)
sim.inc_counter("retries")
} else {
ignore(
sim.schedule_at(tick, "attempt:" + attempt.to_string() + ":succeeded"),
)
sim.inc_counter("success")
success = true
break
}
}
ignore(sim.run_until_idle())
{
attempts: sim.metrics().counter("attempts"),
retries: sim.metrics().counter("retries"),
success,
final_tick: sim.time(),
digest: sim.digest(),
trace: sim.trace(),
}
}
///|
pub(all) struct TrafficConfig {
seed : UInt64
cycles : Int
cars : Int
cycle_ticks : Int
}
///|
pub(all) struct TrafficResult {
cars : Int
green_phases : Int
red_phases : Int
events_executed : Int
final_tick : Int
digest : UInt64
}
///|
pub fn traffic_config(
seed? : UInt64 = 300UL,
cycles? : Int = 8,
cars? : Int = 12,
cycle_ticks? : Int = 5,
) -> TrafficConfig {
{
seed,
cycles: if cycles < 1 {
1
} else {
cycles
},
cars: if cars < 0 {
0
} else {
cars
},
cycle_ticks: if cycle_ticks < 1 {
1
} else {
cycle_ticks
},
}
}
///|
pub fn run_traffic_model(config : TrafficConfig) -> TrafficResult {
let sim = Sim::new(seed=config.seed)
let states = ["green", "yellow", "red", "red"]
ignore(
sim.install_timer(
timer_plan(
"light:cycle",
interval=config.cycle_ticks,
times=config.cycles,
),
),
)
for i in 0.. ModelSummary {
{
name: "queue",
digest: result.digest,
final_tick: result.final_tick,
events: result.events_executed,
}
}
///|
pub fn retry_summary(result : RetryResult) -> ModelSummary {
{
name: "retry",
digest: result.digest,
final_tick: result.final_tick,
events: result.attempts + result.retries,
}
}
///|
pub fn traffic_summary(result : TrafficResult) -> ModelSummary {
{
name: "traffic",
digest: result.digest,
final_tick: result.final_tick,
events: result.events_executed,
}
}
///|
pub fn ModelSummary::line(self : ModelSummary) -> String {
self.name +
" tick=" +
self.final_tick.to_string() +
" events=" +
self.events.to_string() +
" digest=" +
self.digest.to_string()
}
///|
pub(all) struct NetworkConfig {
seed : UInt64
messages : Int
latency_min : Int
latency_max : Int
drop_percent : Int
retry_delay : Int
}
///|
pub(all) struct NetworkResult {
messages : Int
delivered : Int
dropped : Int
retries : Int
pending : Int
final_tick : Int
digest : UInt64
trace : Array[TraceEntry]
}
///|
pub fn network_config(
seed? : UInt64 = 707UL,
messages? : Int = 8,
latency_min? : Int = 1,
latency_max? : Int = 5,
drop_percent? : Int = 20,
retry_delay? : Int = 3,
) -> NetworkConfig {
{
seed,
messages: if messages < 0 {
0
} else {
messages
},
latency_min: if latency_min < 0 {
0
} else {
latency_min
},
latency_max: if latency_max <= latency_min {
latency_min + 1
} else {
latency_max
},
drop_percent: clamp_percent(drop_percent),
retry_delay: if retry_delay < 0 {
0
} else {
retry_delay
},
}
}
///|
fn clamp_percent(value : Int) -> Int {
if value < 0 {
0
} else if value > 100 {
100
} else {
value
}
}
///|
pub fn run_network_model(config : NetworkConfig) -> NetworkResult {
let sim = Sim::new(seed=config.seed)
let bus = MessageBus::new()
for i in 0.. ModelSummary {
{
name: "network",
digest: result.digest,
final_tick: result.final_tick,
events: result.delivered + result.dropped + result.retries,
}
}
///|
pub(all) struct LoadBalancerConfig {
seed : UInt64
jobs : Int
workers : Int
max_arrival_gap : Int
min_service : Int
max_service : Int
strategy : String
}
///|
pub(all) struct LoadBalancerResult {
jobs : Int
workers : Int
strategy : String
final_tick : Int
max_queue_depth : Int
total_wait : Int
events_executed : Int
digest : UInt64
}
///|
pub fn load_balancer_config(
seed? : UInt64 = 808UL,
jobs? : Int = 12,
workers? : Int = 3,
max_arrival_gap? : Int = 3,
min_service? : Int = 2,
max_service? : Int = 6,
strategy? : String = "least_queue",
) -> LoadBalancerConfig {
let normalized_min_service = if min_service < 1 { 1 } else { min_service }
{
seed,
jobs: if jobs < 0 {
0
} else {
jobs
},
workers: if workers < 1 {
1
} else {
workers
},
max_arrival_gap: if max_arrival_gap < 1 {
1
} else {
max_arrival_gap
},
min_service: normalized_min_service,
max_service: if max_service <= normalized_min_service {
normalized_min_service + 1
} else {
max_service
},
strategy,
}
}
///|
pub fn run_load_balancer_model(
config : LoadBalancerConfig,
) -> LoadBalancerResult {
let sim = Sim::new(seed=config.seed)
let available : Array[Int] = []
let assigned : Array[Int] = []
for _ in 0.. arrival {
available[worker]
} else {
arrival
}
let wait = start - arrival
let service = sim.next_range(config.min_service, config.max_service + 1)
let finish = start + service
available[worker] = finish
assigned[worker] = assigned[worker] + 1
total_wait += wait
if wait > max_queue_depth {
max_queue_depth = wait
}
ignore(sim.schedule_at(arrival, "job:" + job.to_string() + ":arrive"))
ignore(
sim.schedule_at(
start,
"job:" + job.to_string() + ":worker:" + worker.to_string() + ":start",
),
)
ignore(sim.schedule_at(finish, "job:" + job.to_string() + ":finish"))
sim.inc_counter("jobs")
sim.inc_counter("worker:" + worker.to_string() + ":jobs")
sim.sample("job_wait", wait)
sim.sample("job_service", service)
}
ignore(sim.run_until_idle())
{
jobs: sim.metrics().counter("jobs"),
workers: config.workers,
strategy: config.strategy,
final_tick: sim.time(),
max_queue_depth,
total_wait,
events_executed: sim.metrics().counter("events_executed"),
digest: sim.digest(),
}
}
///|
fn choose_worker(
sim : Sim,
available : Array[Int],
assigned : Array[Int],
strategy : String,
) -> Int {
if strategy == "random" {
sim.next_int(available.length())
} else if strategy == "round_robin" {
let mut total = 0
for count in assigned {
total += count
}
total % available.length()
} else {
let mut best = 0
let mut i = 1
while i < available.length() {
if available[i] < available[best] {
best = i
} else if available[i] == available[best] && assigned[i] < assigned[best] {
best = i
}
i += 1
}
best
}
}
///|
pub fn load_balancer_summary(result : LoadBalancerResult) -> ModelSummary {
{
name: "load_balancer:" + result.strategy,
digest: result.digest,
final_tick: result.final_tick,
events: result.events_executed,
}
}