Skip to content

Commit

Permalink
Separate different-memory-requirements simulation from different-inst…
Browse files Browse the repository at this point in the history
…ance-requirements

Also, generate start auctions for processes with multiple indices instead of multiple redundant
instances on a single index, unless specifically simulating redundancy
  • Loading branch information
amitkgupta committed Jul 27, 2014
1 parent 90297a1 commit 385fa9c
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 79 deletions.
172 changes: 104 additions & 68 deletions simulation/simulation_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package simulation_test

import (
"fmt"

"github.com/cloudfoundry-incubator/auction/auctionrunner"
"github.com/cloudfoundry-incubator/auction/auctiontypes"
"github.com/cloudfoundry-incubator/auction/simulation/visualization"
Expand All @@ -12,7 +14,7 @@ import (

var _ = Ω

var _ = Describe("Auction", func() {
var _ = Describe("Start and Stop Auctions", func() {
var initialDistributions map[int][]auctiontypes.SimulatedInstance

newSimulatedInstance := func(processGuid string, index int, memoryMB int) auctiontypes.SimulatedInstance {
Expand All @@ -25,56 +27,47 @@ var _ = Describe("Auction", func() {
}
}

generateUniqueSimulatedInstances := func(numInstances int, index int, memoryMB int) []auctiontypes.SimulatedInstance {
generateUniqueSingleIndexLRPsWithRedundantSimulatedInstances := func(numInstances int, memoryMB int) []auctiontypes.SimulatedInstance {
instances := []auctiontypes.SimulatedInstance{}
for i := 0; i < numInstances; i++ {
instances = append(instances, newSimulatedInstance(util.NewGrayscaleGuid("AAA"), index, memoryMB))
instances = append(instances, newSimulatedInstance(util.NewGrayscaleGuid("AAA"), 0, memoryMB))
}
return instances
}

generateSimulatedInstancesForProcessGuid := func(processGuid string, numInstances int, index int, memoryMB int) []auctiontypes.SimulatedInstance {
generateSimulatedInstancesForProcessGuidIndex := func(processGuid string, numInstances int, index int, memoryMB int) []auctiontypes.SimulatedInstance {
instances := []auctiontypes.SimulatedInstance{}
for i := 0; i < numInstances; i++ {
instances = append(instances, newSimulatedInstance(processGuid, index, memoryMB))
}
return instances
}

newLRPStartAuction := func(processGuid string, memoryMB int) models.LRPStartAuction {
newLRPStartAuction := func(processGuid string, memoryMB int, index int) models.LRPStartAuction {
return models.LRPStartAuction{
ProcessGuid: processGuid,
InstanceGuid: util.NewGuid("INS"),
MemoryMB: memoryMB,
DiskMB: 1,
Index: 0,
}
}

generateUniqueLRPStartAuctions := func(numInstances int, memoryMB int) []models.LRPStartAuction {
instances := []models.LRPStartAuction{}
for i := 0; i < numInstances; i++ {
instances = append(instances, newLRPStartAuction(util.NewGrayscaleGuid("BBB"), memoryMB))
Index: index,
}
return instances
}

randomSVGColor := func() string {
return []string{"purple", "red", "cyan", "teal", "gray", "blue", "pink", "green", "lime", "orange", "lightseagreen", "brown"}[util.R.Intn(12)]
}

generateLRPStartAuctionsWithRandomSVGColors := func(numInstances int, memoryMB int) []models.LRPStartAuction {
generateLRPStartAuctionsForProcessGuid := func(numInstances int, processGuid string, memoryMB int) []models.LRPStartAuction {
instances := []models.LRPStartAuction{}
for i := 0; i < numInstances; i++ {
instances = append(instances, newLRPStartAuction(randomSVGColor(), memoryMB))
instances = append(instances, newLRPStartAuction(processGuid, memoryMB, i))
}
return instances
}

generateLRPStartAuctionsForProcessGuid := func(numInstances int, processGuid string, memoryMB int) []models.LRPStartAuction {
generateUniqueLRPStartAuctions := func(numLRPs int, memoryMB int, numInstancesPerLRP int) []models.LRPStartAuction {
instances := []models.LRPStartAuction{}
for i := 0; i < numInstances; i++ {
instances = append(instances, newLRPStartAuction(processGuid, memoryMB))
for i := 0; i < numLRPs; i++ {
instances = append(
instances,
generateLRPStartAuctionsForProcessGuid(numInstancesPerLRP, util.NewGrayscaleGuid("BBB"), memoryMB)...,
)
}
return instances
}
Expand All @@ -90,24 +83,28 @@ var _ = Describe("Auction", func() {
}
})

Describe("Experiments", func() {
Context("Cold start scenario", func() {
Describe("Start Auction Simulations", func() {
Context("With no process running on any executor initially", func() {
nexec := []int{25, 100}
n1apps := []int{1800, 7000}
n2apps := []int{200, 1000}
n4apps := []int{50, 200}

n1MBapps := []int{1800, 7000}
n2MBapps := []int{200, 1000}
n4MBapps := []int{50, 200}

n1INSTapps := []int{1800, 7000}
n2INSTapps := []int{200, 1000}
n4INSTapps := []int{50, 200}

for i := range nexec {
i := i

Context("with variable memory requirements between apps", func() {
It("should distribute evenly", func() {
instances := []models.LRPStartAuction{}

instances = append(instances, generateUniqueLRPStartAuctions(n1apps[i]/2, 1)...)
instances = append(instances, generateLRPStartAuctionsWithRandomSVGColors(n1apps[i]/2, 1)...)
instances = append(instances, generateUniqueLRPStartAuctions(n2apps[i]/2, 2)...)
instances = append(instances, generateLRPStartAuctionsWithRandomSVGColors(n2apps[i]/2, 2)...)
instances = append(instances, generateUniqueLRPStartAuctions(n4apps[i]/2, 4)...)
instances = append(instances, generateLRPStartAuctionsWithRandomSVGColors(n4apps[i]/2, 4)...)
instances = append(instances, generateUniqueLRPStartAuctions(n1MBapps[i], 1, 1)...)
instances = append(instances, generateUniqueLRPStartAuctions(n2MBapps[i], 2, 1)...)
instances = append(instances, generateUniqueLRPStartAuctions(n4MBapps[i], 4, 1)...)

permutedInstances := make([]models.LRPStartAuction, len(instances))
for i, index := range util.R.Perm(len(instances)) {
Expand All @@ -133,25 +130,60 @@ var _ = Describe("Auction", func() {
reports = append(reports, report)
})
})

Context("with variable instance requirements between apps", func() {
It("should distribute evenly", func() {
instances := []models.LRPStartAuction{}

instances = append(instances, generateUniqueLRPStartAuctions(n1INSTapps[i], 1, 1)...)
instances = append(instances, generateUniqueLRPStartAuctions(n2INSTapps[i], 1, 2)...)
instances = append(instances, generateUniqueLRPStartAuctions(n4INSTapps[i], 1, 4)...)

permutedInstances := make([]models.LRPStartAuction, len(instances))
for i, index := range util.R.Perm(len(instances)) {
permutedInstances[i] = instances[index]
}
report := auctionDistributor.HoldAuctionsFor(
"Cold start with variable instance requirements between apps",
nexec[i],
permutedInstances,
repGuids[:nexec[i]],
auctionrunner.DefaultStartAuctionRules,
)

visualization.PrintReport(
numAZs,
client,
report.AuctionResults,
repGuids[:nexec[i]],
report.AuctionDuration,
auctionrunner.DefaultStartAuctionRules,
)

svgReport.DrawReportCard(i, 0, report)
reports = append(reports, report)
})
})
}
})

Context("Imbalanced scenario (e.g. a deploy)", func() {
Context("With some executors empty, and the remaining executors running many perfectly-balanced process instances", func() {
nexec := []int{100, 100}
nempty := []int{5, 1}
napps := []int{500, 100}

for i := range nexec {
i := i
Context("scenario", func() {

Context(fmt.Sprintf("%d Executors, %d Initially Empty, %d Process", nexec[i], nempty[i], napps[i]), func() {
BeforeEach(func() {
for j := 0; j < nexec[i]-nempty[i]; j++ {
initialDistributions[j] = generateUniqueSimulatedInstances(50, 0, 1)
initialDistributions[j] = generateUniqueSingleIndexLRPsWithRedundantSimulatedInstances(50, 1)
}
})

It("should distribute evenly", func() {
instances := generateUniqueLRPStartAuctions(napps[i], 1)
instances := generateUniqueLRPStartAuctions(napps[i], 1, 1)

report := auctionDistributor.HoldAuctionsFor(
"Imbalanced scenario (e.g. a deploy)",
Expand All @@ -176,17 +208,19 @@ var _ = Describe("Auction", func() {
}
})

Context("The Watters demo", func() {
Context("With all executors running well-balanced process instances", func() {
nexec := []int{30, 100}
napps := []int{200, 400}
maxInitialProcessesPerExecutor := 80

for i := range nexec {
i := i

Context("scenario", func() {
Context(fmt.Sprintf("%d Executors, roughly %d Initial Processes per Executor, %d New Processes", nexec[i], maxInitialProcessesPerExecutor, napps[i]), func() {
BeforeEach(func() {
for j := 0; j < nexec[i]; j++ {
initialDistributions[j] = generateUniqueSimulatedInstances(util.RandomIntIn(78, 80), 0, 1)
numInstances := util.RandomIntIn(maxInitialProcessesPerExecutor-2, maxInitialProcessesPerExecutor)
initialDistributions[j] = generateUniqueSingleIndexLRPsWithRedundantSimulatedInstances(numInstances, 1)
}
})

Expand Down Expand Up @@ -215,17 +249,19 @@ var _ = Describe("Auction", func() {
})
}
})
})

Context("Stop Auctions", func() {
Describe("Stop Auctions Specifications", func() {
Describe("When more than one instance is running for a particular index of a process", func() {
processGuid := util.NewGrayscaleGuid("AAA")

Context("when there are duplicate instances on executors with disaparate resource availabilities", func() {
Context("when the redundant instances are on executors with different amounts of available resources", func() {
BeforeEach(func() {
initialDistributions[0] = generateUniqueSimulatedInstances(50, 0, 1)
initialDistributions[0] = append(initialDistributions[0], generateSimulatedInstancesForProcessGuid(processGuid, 1, 0, 1)...)
initialDistributions[0] = generateUniqueSingleIndexLRPsWithRedundantSimulatedInstances(50, 1)
initialDistributions[0] = append(initialDistributions[0], generateSimulatedInstancesForProcessGuidIndex(processGuid, 1, 0, 1)...)

initialDistributions[1] = generateUniqueSimulatedInstances(30, 0, 1)
initialDistributions[1] = append(initialDistributions[1], generateSimulatedInstancesForProcessGuid(processGuid, 1, 0, 1)...)
initialDistributions[1] = generateUniqueSingleIndexLRPsWithRedundantSimulatedInstances(30, 1)
initialDistributions[1] = append(initialDistributions[1], generateSimulatedInstancesForProcessGuidIndex(processGuid, 1, 0, 1)...)
})

It("should favor removing the instance from the heavy-laden executor", func() {
Expand All @@ -248,17 +284,17 @@ var _ = Describe("Auction", func() {
})
})

Context("when the executor with more available resources already has another instance of the app running", func() {
Context("when one redundant instance is on an executor with fewer resources, and the other redundant instance is on an executor running another index of the process", func() {
BeforeEach(func() {
initialDistributions[0] = generateUniqueSimulatedInstances(50, 0, 1)
initialDistributions[0] = append(initialDistributions[0], generateSimulatedInstancesForProcessGuid(processGuid, 1, 0, 1)...)
initialDistributions[0] = generateUniqueSingleIndexLRPsWithRedundantSimulatedInstances(50, 1)
initialDistributions[0] = append(initialDistributions[0], generateSimulatedInstancesForProcessGuidIndex(processGuid, 1, 0, 1)...)

initialDistributions[1] = generateUniqueSimulatedInstances(30, 0, 1)
initialDistributions[1] = append(initialDistributions[1], generateSimulatedInstancesForProcessGuid(processGuid, 1, 0, 1)...)
initialDistributions[1] = append(initialDistributions[1], generateSimulatedInstancesForProcessGuid(processGuid, 1, 1, 1)...)
initialDistributions[1] = generateUniqueSingleIndexLRPsWithRedundantSimulatedInstances(30, 1)
initialDistributions[1] = append(initialDistributions[1], generateSimulatedInstancesForProcessGuidIndex(processGuid, 1, 0, 1)...)
initialDistributions[1] = append(initialDistributions[1], generateSimulatedInstancesForProcessGuidIndex(processGuid, 1, 1, 1)...)
})

It("should favor leaving the instance on the more heavy-laden executor", func() {
It("should favor removing the redundant instance from the executor running the additional index of the process", func() {
stopAuctions := []models.LRPStopAuction{
{
ProcessGuid: processGuid,
Expand All @@ -278,16 +314,16 @@ var _ = Describe("Auction", func() {
})
})

Context("when the executor with fewer available resources has two instances running", func() {
Context("when the executor with fewer available resources is running more of the redundant instances", func() {
BeforeEach(func() {
initialDistributions[0] = generateUniqueSimulatedInstances(50, 0, 1)
initialDistributions[0] = append(initialDistributions[0], generateSimulatedInstancesForProcessGuid(processGuid, 1, 0, 1)...)
initialDistributions[0] = generateUniqueSingleIndexLRPsWithRedundantSimulatedInstances(50, 1)
initialDistributions[0] = append(initialDistributions[0], generateSimulatedInstancesForProcessGuidIndex(processGuid, 1, 0, 1)...)

initialDistributions[1] = generateUniqueSimulatedInstances(30, 0, 1)
initialDistributions[1] = append(initialDistributions[1], generateSimulatedInstancesForProcessGuid(processGuid, 2, 0, 1)...)
initialDistributions[1] = generateUniqueSingleIndexLRPsWithRedundantSimulatedInstances(30, 1)
initialDistributions[1] = append(initialDistributions[1], generateSimulatedInstancesForProcessGuidIndex(processGuid, 2, 0, 1)...)
})

It("should favor removing the instance from the heavy-laden executor", func() {
It("should stop all but one instance, keeping that one instance on the executor with more free resources", func() {
stopAuctions := []models.LRPStopAuction{
{
ProcessGuid: processGuid,
Expand All @@ -307,19 +343,19 @@ var _ = Describe("Auction", func() {
})
})

Context("when there are very many duplicate instances out there", func() {
Context("when there are very many redundant instances out there, and the executors initially have the same amount of free resources", func() {
BeforeEach(func() {
initialDistributions[0] = generateSimulatedInstancesForProcessGuid(processGuid, 50, 0, 1)
initialDistributions[0] = append(initialDistributions[0], generateSimulatedInstancesForProcessGuid(processGuid, 90-50, 1, 1)...)
initialDistributions[0] = generateSimulatedInstancesForProcessGuidIndex(processGuid, 50, 0, 1)
initialDistributions[0] = append(initialDistributions[0], generateSimulatedInstancesForProcessGuidIndex(processGuid, 90-50, 1, 1)...)

initialDistributions[1] = generateSimulatedInstancesForProcessGuid(processGuid, 30, 0, 1)
initialDistributions[1] = append(initialDistributions[1], generateSimulatedInstancesForProcessGuid(processGuid, 90-30, 1, 1)...)
initialDistributions[1] = generateSimulatedInstancesForProcessGuidIndex(processGuid, 30, 0, 1)
initialDistributions[1] = append(initialDistributions[1], generateSimulatedInstancesForProcessGuidIndex(processGuid, 90-30, 1, 1)...)

initialDistributions[2] = generateSimulatedInstancesForProcessGuid(processGuid, 70, 0, 1)
initialDistributions[2] = append(initialDistributions[2], generateSimulatedInstancesForProcessGuid(processGuid, 90-70, 1, 1)...)
initialDistributions[2] = generateSimulatedInstancesForProcessGuidIndex(processGuid, 70, 0, 1)
initialDistributions[2] = append(initialDistributions[2], generateSimulatedInstancesForProcessGuidIndex(processGuid, 90-70, 1, 1)...)
})

It("should stop all but 1", func() {
It("should stop all but one instance, keeping that one instance on the executor that ends up with the most free resources", func() {
stopAuctions := []models.LRPStopAuction{
{
ProcessGuid: processGuid,
Expand Down
16 changes: 5 additions & 11 deletions simulation/visualization/print_report.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,7 @@ const defaultStyle = "\x1b[0m"
const boldStyle = "\x1b[1m"
const redColor = "\x1b[91m"
const greenColor = "\x1b[32m"
const yellowColor = "\x1b[33m"
const cyanColor = "\x1b[36m"
const grayColor = "\x1b[90m"
const lightGrayColor = "\x1b[37m"
const purpleColor = "\x1b[35m"

func PrintReport(
client auctiontypes.SimulationRepPoolClient,
Expand Down Expand Up @@ -131,8 +127,6 @@ func PrintReport(
memory, disk := 0, 0
containersData = append(containersData, float64(len(instances)))

availableColors := []string{"red", "cyan", "yellow", "gray", "purple", "green"}
colorLookup := map[string]string{"red": redColor, "green": greenColor, "cyan": cyanColor, "yellow": yellowColor, "gray": lightGrayColor, "purple": purpleColor}
originalCounts := map[string]int{}
newCounts := map[string]int{}

Expand All @@ -141,7 +135,7 @@ func PrintReport(
disk += instance.DiskMB

key := "green"
if _, ok := colorLookup[instance.ProcessGuid]; ok {
if instance.ProcessGuid == "red" {
key = instance.ProcessGuid
}
if auctionedInstances[instance.InstanceGuid] {
Expand All @@ -156,10 +150,10 @@ func PrintReport(
diskData = append(diskData, float64(disk))

instanceString := ""
for _, col := range availableColors {
instanceString += strings.Repeat(colorLookup[col]+"○"+defaultStyle, originalCounts[col])
instanceString += strings.Repeat(colorLookup[col]+"●"+defaultStyle, newCounts[col])
}
instanceString += strings.Repeat(greenColor+"○"+defaultStyle, originalCounts["green"])
instanceString += strings.Repeat(redColor+"○"+defaultStyle, originalCounts["red"])
instanceString += strings.Repeat(greenColor+"●"+defaultStyle, newCounts["green"])
instanceString += strings.Repeat(redColor+"●"+defaultStyle, newCounts["red"])
instanceString += strings.Repeat(grayColor+"○"+defaultStyle, client.TotalResources(repGuid).Containers-len(instances))

containerHistogramLines = append(containerHistogramLines, fmt.Sprintf(" %s: %s", fmt.Sprintf(guidFormat, repGuid), instanceString))
Expand Down

0 comments on commit 385fa9c

Please sign in to comment.