Created
April 21, 2018 10:28
-
-
Save macdja38/6943f522130fdda6238d12e7b6980793 to your computer and use it in GitHub Desktop.
FoundationDB's python tutorial ported to python 3, running on a process pool instead of a 1 thread per student.
This file contains hidden or 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
import itertools | |
import random | |
import fdb | |
from functools import partial | |
import time | |
from multiprocessing import Pool | |
verbose = False | |
fdb.api_version(510) | |
################################## | |
# Initialization # | |
################################## | |
# Data model: | |
# ('attends', student, class) = '' | |
# ('class', class_name) = seats_left | |
db = fdb.open() | |
scheduling = fdb.directory.create_or_open(db, ('scheduling',)) | |
course = scheduling['class'] | |
attends = scheduling['attends'] | |
@fdb.transactional | |
def add_class(tr, c): | |
tr[course.pack((c,))] = int.to_bytes(100, 2, byteorder="big") | |
# Generate 1,620 classes like '9:00 chem for dummies' | |
levels = ['intro', 'for dummies', 'remedial', '101', | |
'201', '301', 'mastery', 'lab', 'seminar'] | |
types = ['chem', 'bio', 'cs', 'geometry', 'calc', | |
'alg', 'film', 'music', 'art', 'dance'] | |
times = [str(h) + ':00' for h in range(2, 20)] | |
class_combos = itertools.product(times, types, levels) | |
class_names = [' '.join(tup) for tup in class_combos] | |
@fdb.transactional | |
def init(tr): | |
del tr[scheduling.range(())] # Clear the directory | |
for class_name in class_names: | |
add_class(tr, class_name) | |
################################## | |
# Class Scheduling Functions # | |
################################## | |
def v_print(thing): | |
if verbose: | |
print(thing) | |
@fdb.transactional | |
def available_classes(tr): | |
return [course.unpack(k)[0] for k, v in tr[course.range(())] | |
if int.from_bytes(v, byteorder="big")] | |
@fdb.transactional | |
def classes_and_count(tr): | |
return [(course.unpack(k)[0], int.from_bytes(v, byteorder="big")) for k, v in tr[course.range(())]] | |
@fdb.transactional | |
def signup(tr, s, c): | |
v_print("adding " + s + " to " + c) | |
rec = attends.pack((s, c)) | |
if tr[rec].present(): | |
return # already signed up | |
seats_left = int.from_bytes(tr[course.pack((c,))], byteorder="big") | |
if not seats_left: | |
raise Exception('No remaining seats') | |
classes = tr[attends.range((s,))] | |
if len(list(classes)) == 5: | |
raise Exception('Too many classes') | |
tr[course.pack((c,))] = int.to_bytes(seats_left - 1, 2, byteorder="big") | |
tr[rec] = b'' | |
@fdb.transactional | |
def drop(tr, s, c): | |
v_print("dropping " + s + " from " + c) | |
rec = attends.pack((s, c)) | |
if not tr[rec].present(): | |
return # not taking this class | |
try: | |
tr[course.pack((c,))] = int.to_bytes( | |
int.from_bytes(tr[course.pack((c,))], byteorder="big") + 1, | |
2, | |
byteorder="big") | |
except Exception as e: | |
print(e) | |
del tr[rec] | |
@fdb.transactional | |
def switch(tr, s, old_c, new_c): | |
v_print("Switching " + s + " from " + old_c + " to " + new_c) | |
drop(tr, s, old_c) | |
signup(tr, s, new_c) | |
################################## | |
# Testing # | |
################################## | |
def indecisive_student(ops, i): | |
v_print("creating student with id{:d}".format(i)) | |
student_ID = 's{:d}'.format(i) | |
all_classes = class_names | |
my_classes = [] | |
for i in range(ops): | |
class_count = len(my_classes) | |
moods = [] | |
if class_count: | |
moods.extend(['drop', 'switch']) | |
if class_count < 5: | |
moods.append('add') | |
mood = random.choice(moods) | |
try: | |
if not all_classes: | |
all_classes = available_classes(db) | |
if mood == 'add': | |
c = random.choice(all_classes) | |
signup(db, student_ID, c) | |
my_classes.append(c) | |
elif mood == 'drop': | |
c = random.choice(my_classes) | |
drop(db, student_ID, c) | |
my_classes.remove(c) | |
elif mood == 'switch': | |
old_c = random.choice(my_classes) | |
new_c = random.choice(all_classes) | |
switch(db, student_ID, old_c, new_c) | |
my_classes.remove(old_c) | |
my_classes.append(new_c) | |
except Exception as e: | |
print(e, "Need to recheck available classes.") | |
all_classes = [] | |
def run(students, ops_per_student): | |
t1 = time.time() | |
pool = Pool(8) | |
p_indecisive_student = partial(indecisive_student, ops_per_student) | |
pool.map(p_indecisive_student, range(0, students)) | |
print("took {:f}".format(1000 * (time.time() - t1))) | |
print("Ran", students * ops_per_student, "transactions") | |
if __name__ == "__main__": | |
init(db) | |
print(available_classes(db)) | |
print("initialized") | |
run(1000, 10) | |
print(classes_and_count(db)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment