Instantly share code, notes, and snippets.
Last active
July 19, 2020 23:14
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save Sharadh/09b1768983b3ad2bf4f36c3b336d5b67 to your computer and use it in GitHub Desktop.
Share Auto Simulation that leverages some go concurrency patterns for better batching
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"math/rand" | |
"time" | |
) | |
const ( | |
simulationDurationSeconds = 10 | |
autoCapacity = 12 | |
autoDispatchMaxMilliseconds = 10 | |
maxPassengersWaiting = 5000 | |
passengerMaxWaitDuration = time.Duration(50) * time.Millisecond | |
numPassengers = 5000 | |
) | |
type bufferItem struct { | |
val *int | |
done chan<- bool | |
} | |
type bufferBatchItem []bufferItem | |
type sendResult string | |
// makePassenger makes a passenger at some random point | |
// during the simulation. | |
func makePassenger(val int, buffer chan<- bufferItem, result chan<- sendResult) { | |
time.Sleep(time.Duration(rand.Intn(simulationDurationSeconds)) * time.Second) | |
// Adding a buffer size of 1 makes the send non-blocking. | |
done := make(chan bool, 1) | |
buffer <- bufferItem{&val, done} | |
result <- "sent" | |
// Block until done; only exit on confirmation of buffering. | |
<-done | |
result <- "done" | |
} | |
// gatherPassengers waits for 10 passengers or a timeout | |
// and sends whoever it has at that point. | |
func gatherPassengers(buffer <-chan bufferItem) { | |
passengers := make([]bufferItem, autoCapacity) | |
var count uint | |
for i := 1; ; i++ { | |
// Wait for the first passenger. | |
count = 0 | |
item := <-buffer | |
//fmt.Printf("Received first passenger: %d\n", p.val) | |
passengers[count] = item | |
count++ | |
// Start the timer. | |
fmt.Printf("Will wait for 50ms before leaving...\n") | |
timeout := time.After(passengerMaxWaitDuration) | |
// Collect messages until batch complete, or timeout. | |
waitForPassengers: | |
for ; count < autoCapacity; count++ { | |
select { | |
case <-timeout: | |
fmt.Printf("Time to leave!\n") | |
break waitForPassengers | |
case item = <-buffer: | |
//fmt.Printf("Received another passenger: %d\n", p.val) | |
passengers[count] = item | |
} | |
} | |
arrived := make([]bufferItem, count) | |
copy(arrived, passengers[:count]) | |
go dispatchAuto(i, arrived) | |
} | |
} | |
// dispatchAuto will put a batch of passengers into an auto and leave. | |
func dispatchAuto(autoId int, passengers bufferBatchItem) { | |
// Leave. | |
fmt.Printf("Honk Honk! Auto %d leaving with %d passengers.\n", autoId, len(passengers)) | |
time.Sleep(time.Duration(rand.Intn(autoDispatchMaxMilliseconds)) * time.Millisecond) | |
// Notify. | |
for _, p := range passengers { | |
p.done <- true | |
} | |
} | |
// main runs the shareAuto simulation. | |
// | |
// The shareAuto concept is simple - the shareAuto (tuk-tuk) can hold | |
// up to 10 passengers (seated on park benches - very safe!) and drivers | |
// really want to do trips with as many passengers as possible. | |
// However, it's not comfortable waiting inside the auto (it's ok if | |
// it's moving though!) so passengers will leave if they done for too | |
// long. | |
// | |
// Our well-natured dispatcher promises that no passenger will ever done | |
// more than a set amount of time; at that point, the auto will leave | |
// regardless of how full it is. This, our dispatcher assures, is the way | |
// to the optimal blend of ₹₹₹ as well as Net Promoter Score (NPS). | |
func main() { | |
fmt.Printf("Starting %d sec shareAuto simulation for %d passengers...\n", simulationDurationSeconds, numPassengers) | |
rand.Seed(time.Now().Unix()) | |
buffer := make(chan bufferItem, maxPassengersWaiting) | |
go gatherPassengers(buffer) | |
results := make(chan sendResult, numPassengers) | |
for i := 0; i < numPassengers; i++ { | |
go makePassenger(i, buffer, results) | |
} | |
var sentCount, doneCount uint = 0, 0 | |
exitTimer := time.After(time.Duration(simulationDurationSeconds+1) * time.Second) | |
countUntilExit: | |
for { | |
select { | |
case r := <-results: | |
if r == "sent" { | |
sentCount++ | |
} else if r == "done" { | |
doneCount++ | |
} | |
case <-exitTimer: | |
fmt.Println("Time's up!") | |
break countUntilExit | |
} | |
} | |
fmt.Printf("Dispatched %d/%d passengers\n", doneCount, sentCount) | |
fmt.Println("Bye bye!") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment