Skip to content

Instantly share code, notes, and snippets.

@macdja38
Created April 21, 2018 10:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save macdja38/6943f522130fdda6238d12e7b6980793 to your computer and use it in GitHub Desktop.
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.
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