Skip to content

Commit

Permalink
šŸž Hotfix/Split getValidExperiment queries into 3 sub queries to reducā€¦
Browse files Browse the repository at this point in the history
ā€¦e latency against Release V5 (#1083)

* split getValidExperiment queries into 3 sub queries to reduce latency

* added cache for validExperiment function

* āš” Fixing caching

Updating caching-manager and fixing caching bug

* Run all queries at once for getValidExperiments

* solved caching errors

* revert cache-manger to older version

* fix majority of unit test cases for cached valid experiment mocks

* solve one of the test suite failures with appropriate mock

* fix for final test suite

* remove unnecessory console.logs

* solve git comments

* remove promise.all for sigle promises and remove metrics query from getValidExps as its not being used in assign and mark

* add CACHING_TTL to env

* add omnibus-specific load-test file to the repo

---------

Co-authored-by: RidhamShah <[email protected]>
Co-authored-by: Vivek Fitkariwala <[email protected]>
Co-authored-by: danoswaltCL <[email protected]>
  • Loading branch information
4 people authored Nov 15, 2023
1 parent e7d1d89 commit 18fff76
Show file tree
Hide file tree
Showing 16 changed files with 716 additions and 208 deletions.
419 changes: 419 additions & 0 deletions backend/locust/load_test_omnibus.py

Large diffs are not rendered by default.

71 changes: 16 additions & 55 deletions backend/locust/load_test_upgrade_mathia.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,22 @@

schools = {}
students = {}

allExperimentPartitionIDConditionPair = []
# For load testing on existing prod experiments:
# allExperimentPartitionIDConditionPair = [
# {"experimentPoint": "SelectAdaptive", "partitionId" : "1_0gzegepj", "condition" : "human-robert-control"},
# {"experimentPoint": "SelectAdaptive", "partitionId" : "1_ffi961t9", "condition" : "human-robert-control"},
# {"experimentPoint": "SelectAdaptive", "partitionId" : "1_m7qve72i", "condition" : "joke-ai-robert-experimental"},
# {"experimentPoint": "SelectAdaptive", "partitionId" : "1_2ee7ws2b", "condition" : "joke-ai-robert-experimental"},
# {"experimentPoint": "SelectAdaptive", "partitionId" : "1_i7i7heqa", "condition" : "control-playback-speed"},
# {"experimentPoint": "SelectAdaptive", "partitionId" : "1_yspx09fg", "condition" : "control-playback-speed"},
# {"experimentPoint": "SelectAdaptive", "partitionId" : "1_fwk036l9", "condition" : "control-playback-speed"},
# {"experimentPoint": "DisplayQuestion", "partitionId" : "AR-VST-0150-6.NS.8-1_SP_014", "condition" : "learnosity-item-control"},
# {"experimentPoint": "DisplayQuestion", "partitionId" : "AR-VST-0150-6.NS.8-1_SP_008", "condition" : "learnosity-item-control"},
# {"experimentPoint": "DisplayQuestion", "partitionId" : "AR-VST-0150-6.NS.8-1_SP_004", "condition" : "learnosity-item-control"},
# {"experimentPoint": "SelectStream", "partitionId" : "HintUI", "condition" : "question-hint-tutorbot"},
# {"experimentPoint": "SelectStream", "partitionId" : "rewindButton", "condition" : "enable-rewind-button"}
# ]

# Setting host URL's:
protocol = "http"
Expand Down Expand Up @@ -48,30 +62,24 @@ def initStudent():
numClasses = [random.choices([1, 2], [63, 37])[0]]
else:
numClasses = [random.choices([1, 2], [63, 37])[0], random.choices([1, 2], [63, 37])[0]]

schoolIds = getSchools(schoolCount)
classData = getClasses(schoolIds, numClasses)

students[studentId] = {
"studentId": studentId,
"schools": {}
}

for schoolId in schoolIds:
students[studentId]["schools"][schoolId] = {
"classes": {},
"instructors": []
}

for classObject in classData:
students[studentId]["schools"][classObject["schoolId"]]["classes"][classObject["classId"]] = {
"classId": classObject["classId"],
"instructorId": classObject["instructorId"],
"classModules": classObject["classModules"]
}

return students[studentId]

#Return a list of schools, either new or existing
def getSchools(schoolCount):
retSchools = []
Expand All @@ -85,30 +93,23 @@ def getSchools(schoolCount):
# if schoolCount is between 10 and 2140, create a new school 50% of the time
else:
createNew = random.choices([True, False], [50, 50])[0]

if createNew:
schoolId = str(uuid.uuid4())
while schoolId in retSchools:
schoolId = str(uuid.uuid4())

instructors = []
for i in range(10):
instructors.append(str(uuid.uuid4()))

schools[schoolId] = {
"classes": {},
"instructors": instructors
}

else:
schoolId = random.choice(list(schools.keys()))
while schoolId in retSchools:
schoolId = random.choice(list(schools.keys()))

retSchools.append(schoolId)

return retSchools

#Return a list of classes, either new or existing
def getClasses(schoolIds, numClasses):
retClasses = []
Expand All @@ -125,12 +126,10 @@ def getClasses(schoolIds, numClasses):
else:
#if numClasses is between 5 and 50, create a new class 50% of the time
createNew = random.choices([True, False], [50, 50])[0]

if createNew:
classId = str(uuid.uuid4())
while classId in retClasses:
classId = str(uuid.uuid4())

instructorId = random.choice(schools[schoolId]["instructors"])
classModules = random.sample(list(modules.keys()), k=5)
schools[schoolId]["classes"][classId] = {
Expand All @@ -139,27 +138,20 @@ def getClasses(schoolIds, numClasses):
"instructorId": instructorId,
"classModules": classModules
}

else:
classId = random.choice(list(schools[schoolId]["classes"].keys()))
while classId in retClasses:
classId = random.choice(list(schools[schoolId]["classes"].keys()))

retClasses.append(classId)
retClassData.append(schools[schoolId]["classes"][classId])

return retClassData

# Main Locust API calls for enrolling students in an experiment:
class UpgradeUserTask(SequentialTaskSet):

# each User represents one Student
def on_start(self):
self.student = initStudent()

#Portal Tasks
## Portal calls init -> setGroupMembership in reality

# Task 1: portal calls /init
@tag("required", "portal")
@task
Expand All @@ -171,26 +163,21 @@ def init(self):
"group": {},
"workingGroup": {}
}

with self.client.post(url, json = data, catch_response = True) as response:
if response.status_code != 200:
print(f"Init Failed with {response.status_code} for userid: " + self.student["studentId"])

# Task 2: portal calls /groupmembership
@tag("portal")
@task
def setGroupMembership(self):
schoolIds = list(self.student["schools"].keys())

classIds = []
for schoolId in self.student["schools"].keys():
classIds.extend(list(self.student["schools"][schoolId]["classes"].keys()))

instructorIds = []
for schoolId in self.student["schools"].keys():
for classId in self.student["schools"][schoolId]["classes"].keys():
instructorIds.append(self.student["schools"][schoolId]["classes"][classId]["instructorId"])

url = protocol + f"://{host}/api/groupmembership"
print("/groupmembership for userid: " + self.student["studentId"])
data = {
Expand All @@ -201,12 +188,9 @@ def setGroupMembership(self):
"instructorId": instructorIds
}
}

with self.client.post(url, json = data, catch_response = True) as response:
if response.status_code != 200:
print(f"Group membership Failed with {response.status_code} for userid: " + self.student["studentId"])


# Task 3: portal calls /assign
@tag("portal")
@task
Expand All @@ -217,12 +201,9 @@ def getAllExperimentConditionsPortal(self):
"userId": self.student["studentId"],
"context": "portal"
}

with self.client.post(url, json = data, catch_response = True) as response:
if response.status_code != 200:
print(f"/assign Failed with {response.status_code} for userid: " + self.student["studentId"])


# Launcher tasks
# Task 4: launcher calls /workinggroup
@tag("launcher")
Expand All @@ -241,50 +222,40 @@ def setWorkingGroup(self):
"instructorId": workingInstructorId
}
}

with self.client.post(url, json = data, catch_response = True) as response:
if response.status_code != 200:
print(f"setWorkingGroup Failed with {response.status_code} for userid: " + self.student["studentId"])

# Task 5: launcher calls /useraliases
@tag("launcher")
@task
def setAltIds(self):
workingSchoolId = random.choice(list(self.student["schools"].keys()))
workingClassId = random.choice(list(self.student["schools"][workingSchoolId]["classes"].keys()))
classModules = self.student["schools"][workingSchoolId]["classes"][workingClassId]["classModules"]

url = protocol + f"://{host}/api/useraliases"
print("/useraliases for userid: " + self.student["studentId"])
data = {
"userId": self.student["studentId"],
"aliases": [self.student["studentId"] + m for m in classModules]
}

with self.client.post(url, json = data, catch_response = True) as response:
if response.status_code != 200:
print(f"/useraliases Failed with {response.status_code} for userid: " + self.student["studentId"])


#Assignment Progress Service
#Skipping getExperimentCondition() - Assume getAllExperimentConditionsAssignProg() has been called, so getExperimentCondition() does not hit API

# Task 6: workspace calls /assign or uses cached data
@tag("assign-prog")
@task
def getAllExperimentConditionsAssignProg(self):
url = protocol + f"://{host}/api/assign"
print("/assign assign-prog for userid: " + self.student["studentId"])

data = {
"userId": self.student["studentId"],
"context": "assign-prog"
"context": "mathstream"
}

with self.client.post(url, json = data, catch_response = True) as response:
if response.status_code != 200:
print(f"getAllExperimentConditions in assign-prog Failed with {response.status_code}")

# mark is called after finishing a workspace. In reality, mark is called 15-30 mins after assign
# Task 7: Student count gets incremented here on marking complete
@tag("assign-prog")
Expand All @@ -308,18 +279,15 @@ def markExperimentPoint(self):

# pick a random assigned workspace - requires /assign response to be saved
# markPartitionIDConditionPair = random.choice(self.assignedWorkspaces)

# data = {
# "userId": self.student["studentId"],
# "experimentPoint": markPartitionIDConditionPair['expPoint'],
# "partitionId": markPartitionIDConditionPair['expId'],
# "condition": markPartitionIDConditionPair['assignedCondition']['conditionCode']
# }

with self.client.post(url, json = data, catch_response = True) as response:
if response.status_code != 200:
print(f"/mark Failed with {response.status_code} for userid: " + self.student["studentId"])

# Task 8: failed experiment point
@tag("assign-prog")
@task
Expand All @@ -343,18 +311,15 @@ def failedExperimentPoint(self):

# pick a random assigned workspace - requires /assign response to be saved
# markPartitionIDConditionPair = random.choice(self.assignedWorkspaces)

# data = {
# "reason": "locust tests",
# "experimentPoint": markPartitionIDConditionPair['expPoint'],
# "userId": self.student["studentId"],
# "experimentId": markPartitionIDConditionPair['expId']
# }

with self.client.post(url, json = data, catch_response = True) as response:
if response.status_code != 200:
print(f"/failed Failed with {response.status_code} for userid: " + self.student["studentId"])

# Generate mock log data
def genMockLog(self):
attributes = {
Expand Down Expand Up @@ -386,7 +351,6 @@ def genMockLog(self):
"groupedMetrics": [groupedMetrics]
}
}

#UpgradeForwarder
# Task 9:
@tag("logger")
Expand All @@ -399,12 +363,9 @@ def logEvent(self):
self.genMockLog()
]
}

with self.client.post(url, json = data, catch_response = True) as response:
if response.status_code != 200:
print(f"LogEvent Failed with {response.status_code}")


class UpgradeUser(HttpUser):
wait_time = between(0.1, 10)
host = "localhost:3030"
Expand Down
1 change: 1 addition & 0 deletions backend/packages/Upgrade/.env.docker.local
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ APP_ROUTE_PREFIX=/api
APP_BANNER=true
APP_DEMO=false
CACHING_ENABLED=true
CACHING_TTL=10

#
# LOGGING
Expand Down
3 changes: 2 additions & 1 deletion backend/packages/Upgrade/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ APP_PORT=3030
APP_ROUTE_PREFIX=/api
APP_BANNER=true
APP_DEMO=false

CACHING_ENABLED=true
CACHING_TTL=10
#
# LOGGING
#
Expand Down
2 changes: 2 additions & 0 deletions backend/packages/Upgrade/.env.test
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ APP_PORT=3030
APP_ROUTE_PREFIX=/api
APP_BANNER=true
APP_DEMO=false
CACHING_ENABLED=true
CACHING_TTL=10

#
# LOGGING
Expand Down
16 changes: 8 additions & 8 deletions backend/packages/Upgrade/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 18fff76

Please sign in to comment.