Random queue simulation
Overview of the random queue simulation app.
This example demonstrates the use of Nextmv's discrete event simulator, Dash, for customers using Nextmv Enterprise.
In this example, we generalize the single-server queue by randomly generating customers with interarrival times and service times from discrete uniform distributions. Interarrival time is just the time between subsequent arrivals.
Randomization helps us to capture the uncertainty of real-world problems.

Input Data

Instead of using the input to specify deterministic customers, we define two arrays of integer values: interarrival times and service times. Our simulation will later generate users by sampling with replacement from each of these lists. We also specify the maximum number of customers to generate (if time permits).
JSON
1
{
2
"InterarrivalTimes": [5, 10, 3, 7, 8, 7, 2],
3
"ServiceTimes": [20, 30, 40, 25, 15],
4
"NumCustomers": 100
5
}
Copied!

Configuration

In the previous example, we used the CLI runner to load our customers from input data. In this version of the model, we generate customers instead of specifying them in the input data, so we define a struct to hold the number of customers and distribution of times.
Go
1
package main
2
3
import (
4
"time"
5
6
"github.com/nextmv-io/code/dash/sim/rand"
7
)
8
9
type config struct {
10
InterarrivalTimes []int
11
ServiceTimes []int
12
NumCustomers int
13
14
samplers struct {
15
interarrival sample.Sampler
16
service sample.Sampler
17
}
18
}
Copied!
The first three values will be automatically unmarshaled from the JSON. To initialize the samplers, we define a method on this struct:
Go
1
func (c *config) init() {
2
c.samplers.interarrival = sample.WithReplacement(c.InterarrivalTimes)
3
c.samplers.service = sample.WithReplacement(c.ServiceTimes)
4
}
Copied!

Customers

The customer struct in this example is nearly identical to the single server queue:
Go
1
type customer struct {
2
Number int
3
Interarrival time.Duration // minutes after the last customer arrived
4
Service time.Duration
5
6
events ledger.Ledger
7
measures ledger.Ledger
8
9
arrivalTime *time.Time
10
serviceTime *time.Time
11
}
Copied!
The customer Run method and measures also don't have to change from the earlier example. However, we add a new function newCustomer which generates a random customer by sampling from the interarrival time and service time distributions.
Go
1
func newCustomer(num int, cfg *config) *customer {
2
return &customer{
3
Number: num,
4
Interarrival: cfg.interarrival(),
5
Service: cfg.service(),
6
}
7
}
Copied!

Server

This example is able to reuse the same single server code from the previous example.

Runner

The runner in this example changes to handle the dynamic creation of customers. Instead of taking customers as input, the Run function takes a config object. This config object has to be initialized inside the run function to ensure the samplers are initialized with the input values.
The runner creates NumCustomers customers and adds them to the simulator's queue.
Go
1
func main() {
2
3
cli.Run(
4
func(cfg *config, opt sim.Options) (sim.Simulator, error) {
5
cfg.init()
6
7
simulator := sim.New(opt)
8
9
now := time.Now()
10
var cumulativeTime time.Duration
11
12
for i := 0; i < cfg.NumCustomers; i++ {
13
c := newCustomer(i, cfg)
14
15
cumulativeTime += c.Interarrival
16
arrival := now.Add(cumulativeTime)
17
simulator.Add(arrival, c)
18
}
19
20
simulator.Add(now, &server{})
21
22
return simulator, nil
23
},
24
)
25
}
Copied!
We can run this example just like the single-server queue. Using jq, we can look at the wait time measures:
Bash
1
./random-queue -dash.runner.input.path input.json \
2
-dash.simulator.limits.duration 2h \
3
-dash.runner.output.measures | \
4
jq .measures[].measure
Copied!
JSON
1
{ "Number": 0, "Minutes": 0 }
2
{ "Number": 1, "Minutes": 10 }
3
{ "Number": 2, "Minutes": 35 }
4
{ "Number": 3, "Minutes": 58 }
Copied!
Despite this being the randomized simulation example, if you rerun that command, you will get the same output as a result of Go using a default random seed. However, you can set the random seed with the -dash.simulator.random.seed flag.
By setting a new random seed, we get a different simulation
Bash
1
./random-queue -dash.runner.input.path input.json \
2
-dash.simulator.random.seed 234 \
3
-dash.simulator.limits.duration 2h \
4
-dash.runner.output.measures | \
5
jq .measures[].measure
Copied!
JSON
1
{ "Number": 0, "Minutes": 0 }
2
{ "Number": 1, "Minutes": 5 }
3
{ "Number": 2, "Minutes": 17 }
4
{ "Number": 3, "Minutes": 25 }
5
{ "Number": 4, "Minutes": 62 }
6
{ "Number": 5, "Minutes": 77 }
Copied!
To get different result each time, you could set the seed to $(date +%s) or $RANDOM.
Export as PDF
Copy link