1 changed files with 0 additions and 277 deletions
@ -1,277 +0,0 @@
@@ -1,277 +0,0 @@
|
||||
package simulation |
||||
|
||||
import ( |
||||
"math" |
||||
) |
||||
|
||||
type LoadBalancerSpec struct { |
||||
Capacity float64 |
||||
BaseLatency int |
||||
Cost int |
||||
} |
||||
|
||||
type WebServerSmall struct { |
||||
Capacity int |
||||
BaseLatency int |
||||
PenaltyPerRPS float64 |
||||
Cost int |
||||
} |
||||
|
||||
type WebServerMedium struct { |
||||
Capacity int |
||||
BaseLatency int |
||||
PenaltyPerRPS float64 |
||||
Cost int |
||||
} |
||||
|
||||
type CacheStandard struct { |
||||
Capacity int |
||||
BaseLatency int |
||||
PenaltyPer10RPS float64 |
||||
HitRates map[string]float64 |
||||
Cost int |
||||
} |
||||
|
||||
type CacheLarge struct { |
||||
Capacity int |
||||
BaseLatency int |
||||
PenaltyPer10RPS float64 |
||||
HitRates map[string]float64 |
||||
Cost int |
||||
} |
||||
|
||||
type DbReadReplica struct { |
||||
ReadCapacity int // RPS
|
||||
BaseReadLatency int // ms
|
||||
PenaltyPer10RPS float64 |
||||
Cost int |
||||
} |
||||
|
||||
type ComponentSpec struct { |
||||
LoadBalancer LoadBalancerSpec |
||||
WebServerSmall WebServerSmall |
||||
WebServerMedium WebServerMedium |
||||
CacheStandard CacheStandard |
||||
CacheLarge CacheLarge |
||||
DbReadReplica DbReadReplica |
||||
} |
||||
|
||||
type FailureEvent struct { |
||||
Type string |
||||
Time int |
||||
} |
||||
type Level struct { |
||||
ID int |
||||
Description string |
||||
TargetRPS int |
||||
MaxP95Latency int |
||||
MaxMonthlyCost int |
||||
RequiredAvailability int |
||||
FailureEvents []FailureEvent |
||||
ComponentSpec ComponentSpec |
||||
SimulatedDurationSeconds int |
||||
} |
||||
|
||||
type Metrics struct { |
||||
Cost int `json:"cost"` |
||||
P95 float64 `json:"p95"` |
||||
AchievedRPS int `json:"achievedRPS"` |
||||
Availability float64 `json:"availability"` |
||||
} |
||||
|
||||
type EvaluationResult struct { |
||||
Pass bool `json:"pass"` |
||||
Metrics Metrics `json:"metrics"` |
||||
} |
||||
|
||||
type Latencies struct { |
||||
L95WS float64 |
||||
L95Cache float64 |
||||
L95DBRead float64 |
||||
L95TotalRead float64 |
||||
} |
||||
|
||||
type ValidationMetrics struct { |
||||
Cost int |
||||
P95 float64 |
||||
AchievedRPS int |
||||
Availability float64 |
||||
} |
||||
type ValidationResults struct { |
||||
Pass bool |
||||
Reason *string |
||||
Metrics *ValidationMetrics |
||||
} |
||||
|
||||
type CacheType string |
||||
|
||||
const ( |
||||
CacheStandardType CacheType = "cacheStandard" |
||||
CacheLargeType CacheType = "cacheLarge" |
||||
) |
||||
|
||||
type LevelSimulator struct { |
||||
Level Level |
||||
Design Design |
||||
Specs ComponentSpec |
||||
} |
||||
|
||||
func (ls *LevelSimulator) ComputeCost() int { |
||||
s := ls.Specs |
||||
d := ls.Design |
||||
|
||||
costLb := s.LoadBalancer.Cost |
||||
costWSSmall := d.NumWebServerSmall * s.WebServerSmall.Cost |
||||
costWSMedium := d.NumWebServerMedium * s.WebServerSmall.Cost |
||||
var costCache int |
||||
|
||||
if d.CacheType == CacheStandardType { |
||||
costCache = s.CacheStandard.Cost |
||||
} else { |
||||
costCache = s.CacheLarge.Cost |
||||
} |
||||
|
||||
costDB := s.DbReadReplica.Cost * (1 + d.NumDbReplicas) |
||||
|
||||
return costLb + costWSSmall + costWSMedium + costCache + costDB |
||||
} |
||||
|
||||
func (ls *LevelSimulator) ComputeRPS() (float64, float64) { |
||||
s := ls.Specs |
||||
d := ls.Design |
||||
l := ls.Level |
||||
|
||||
totalRPS := l.TargetRPS |
||||
|
||||
var hitRate float64 |
||||
if d.CacheType == CacheStandardType { |
||||
hitRate = s.CacheStandard.HitRates[d.CacheTTL] |
||||
} else if d.CacheType == CacheLargeType { |
||||
hitRate = s.CacheLarge.HitRates[d.CacheTTL] |
||||
} else { |
||||
hitRate = 0.0 |
||||
} |
||||
|
||||
hitRPS := float64(totalRPS) * hitRate |
||||
missesRPS := float64(totalRPS) * (1 - hitRate) |
||||
return hitRPS, missesRPS |
||||
} |
||||
|
||||
func (ls *LevelSimulator) ComputeLatencies() Latencies { |
||||
s := ls.Specs |
||||
d := ls.Design |
||||
|
||||
_, missesRPS := ls.ComputeRPS() |
||||
|
||||
capSmall := s.WebServerSmall.Capacity |
||||
capMedium := s.WebServerMedium.Capacity |
||||
|
||||
weightedCount := d.NumWebServerSmall + (2 * d.NumWebServerSmall) |
||||
|
||||
var L95WS float64 |
||||
if weightedCount == 0 { |
||||
L95WS = math.Inf(1) |
||||
} else { |
||||
loadPerWeighted := missesRPS / float64(weightedCount) |
||||
|
||||
L95WSSmall := 0.0 |
||||
if d.NumWebServerSmall > 0 { |
||||
if loadPerWeighted <= float64(capSmall) { |
||||
L95WSSmall = float64(s.WebServerSmall.BaseLatency) |
||||
} else { |
||||
L95WSSmall = (float64(s.WebServerSmall.BaseLatency) + s.WebServerSmall.PenaltyPerRPS*(loadPerWeighted-float64(capSmall))) |
||||
} |
||||
} |
||||
|
||||
L95WSMedium := 0.0 |
||||
if d.NumWebServerMedium > 0 { |
||||
if loadPerWeighted <= float64(capMedium) { |
||||
L95WSMedium = float64(s.WebServerSmall.BaseLatency) |
||||
} else { |
||||
L95WSMedium = (float64(s.WebServerSmall.BaseLatency) + s.WebServerMedium.PenaltyPerRPS*(loadPerWeighted-float64(capMedium))) |
||||
} |
||||
} |
||||
|
||||
L95WS = math.Max(L95WSSmall, L95WSMedium) |
||||
} |
||||
|
||||
var L95Cache float64 |
||||
if d.CacheType == CacheStandardType { |
||||
L95Cache = float64(s.CacheStandard.BaseLatency) |
||||
} else if d.CacheType == CacheLargeType { |
||||
L95Cache = float64(s.CacheLarge.BaseLatency) |
||||
} else { |
||||
L95Cache = 0.0 |
||||
} |
||||
|
||||
readCap := s.DbReadReplica.ReadCapacity |
||||
baseReadLat := s.DbReadReplica.BaseReadLatency |
||||
penPer10 := s.DbReadReplica.PenaltyPer10RPS |
||||
|
||||
numReps := d.NumDbReplicas |
||||
var L95DbRead float64 |
||||
|
||||
if numReps == 0 { |
||||
if missesRPS <= float64(readCap) { |
||||
L95DbRead = float64(baseReadLat) |
||||
} else { |
||||
excess := missesRPS - float64(readCap) |
||||
L95DbRead = float64(baseReadLat) + penPer10*(excess/10.0) |
||||
} |
||||
} else { |
||||
loadPerRep := missesRPS / float64(numReps) |
||||
if loadPerRep <= float64(readCap) { |
||||
L95DbRead = float64(baseReadLat) |
||||
} else { |
||||
loadPerRep := missesRPS / float64(numReps) |
||||
if loadPerRep <= float64(readCap) { |
||||
L95DbRead = float64(baseReadLat) |
||||
} else { |
||||
excess := loadPerRep - loadPerRep - float64(readCap) |
||||
L95DbRead = float64(baseReadLat) + penPer10*(excess/10.0) |
||||
} |
||||
} |
||||
} |
||||
|
||||
LLb := s.LoadBalancer.BaseLatency |
||||
missPath := LLb + int(L95WS) + int(L95DbRead) |
||||
L95TotalRead := missPath |
||||
|
||||
return Latencies{ |
||||
L95WS: L95WS, |
||||
L95Cache: L95Cache, |
||||
L95DBRead: L95DbRead, |
||||
L95TotalRead: float64(L95TotalRead), |
||||
} |
||||
} |
||||
|
||||
func (ls *LevelSimulator) ComputeAvailability() float64 { |
||||
simDuration := ls.Level.SimulatedDurationSeconds |
||||
totalDownTime := 0 |
||||
for _, event := range ls.Level.FailureEvents { |
||||
tCrash := event.Time |
||||
if event.Type == "DB_MASTER_CRASH" { |
||||
if ls.Design.NumDbReplicas == 0 { |
||||
totalDownTime += (simDuration - tCrash) |
||||
} else { |
||||
delay := ls.Design.PromotionDelaySeconds |
||||
totalDownTime += delay |
||||
} |
||||
} |
||||
} |
||||
|
||||
return (float64(simDuration) - float64(totalDownTime)) / float64(simDuration) * 100 |
||||
} |
||||
|
||||
func (ls *LevelSimulator) Validate() ValidationResults { |
||||
totalCost := ls.ComputeCost() |
||||
if totalCost > ls.Level.MaxMonthlyCost { |
||||
return ValidationResults{ |
||||
Pass: false, |
||||
Metrics: Metrics{ |
||||
Cost: totalCost, |
||||
P95: la, |
||||
}, |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue