Example #1
0
    def post(self):
        global global_dict
        #global_dict = {}
        global global_sync
        global locks
        global last_write
        #global sync_lock

        #        starttime = time.time()
        results = json.loads(self.request.body)

        rumble = results.get("game", "ERROR")

        syncname = str(
            bool(results.get("melee") == "YES")) + "|" + structures.sync
        if syncname not in locks:
            locks[syncname] = threading.Lock()
        sync_lock = locks[syncname]

        global_sync[syncname] = global_sync.get(syncname, {})
        botsync = global_sync[syncname]

        bota = results.get("fname")
        botb = results.get("sname")
        #bota = rreplace(bota,"_"," ",1)
        #botb = rreplace(botb,"_"," ",1)
        logging.debug("Bots : " + bota + " vs. " + botb)

        bd = [[bota, rumble], [botb, rumble]]

        botHashes = [string.join(a, "|").encode('ascii') for a in bd]

        memget = [rumble]
        memget.extend(botHashes)  #botHashes.append(rumble)
        memdict = memcache.get_multi(memget)

        game = memdict.get(rumble, None)
        game_future = None
        if game is None:
            #game = structures.Rumble.get_by_key_name(rumble)
            game_future = db.get_async(db.Key.from_path('Rumble', rumble))

        newBot = False

        bots = [memdict.get(h, None) for h in botHashes]

        pairingsarray = [[], []]
        botFutures = [None, None]
        for i in [0, 1]:
            if bots[i] is None or bots[i].PairingsList is None:
                botFutures[i] = db.get_async(
                    db.Key.from_path('BotEntry', botHashes[i]))

        for i in [0, 1]:
            if botFutures[i] is not None:
                modelbot = botFutures[i].get_result()
                if modelbot is not None:
                    bots[i] = structures.CachedBotEntry(modelbot)
                    #logging.debug("retrieved from database")

        for i in [0, 1]:
            if bots[i] is None:
                modelbot = structures.BotEntry(key_name=botHashes[i],
                                               Name=bd[i][0],
                                               Battles=0,
                                               Pairings=0,
                                               APS=0.0,
                                               Survival=0.0,
                                               PL=0,
                                               Rumble=rumble,
                                               Active=False,
                                               PairingsList=zlib.compress(
                                                   pickle.dumps([]), 1))
                bots[i] = structures.CachedBotEntry(modelbot)
                newBot = True
            if isinstance(bots[i], structures.BotEntry):
                bots[i] = structures.CachedBotEntry(bots[i])

            try:
                pairingsarray[i] = pickle.loads(
                    zlib.decompress(bots[i].PairingsList))
                bots[i].PairingsList = None
            except:
                try:
                    pairsDicts = marshal.loads(
                        zlib.decompress(bots[i].PairingsList))

                    pairingsarray[i] = [
                        structures.ScoreSet() for _ in pairsDicts
                    ]
                    for s, d in zip(pairingsarray[i], pairsDicts):
                        s.__dict__.update(d)
                    bots[i].PairingsList = None
                except:
                    pairingsarray[i] = []

        if game_future is not None:
            game = game_future.get_result()

        if game is None:
            game = structures.Rumble(key_name=rumble,
                                     Name=rumble,
                                     Rounds=int(results["rounds"]),
                                     Field=results["field"],
                                     Melee=bool(results["melee"] == "YES"),
                                     Teams=bool(results["teams"] == "YES"),
                                     TotalUploads=0,
                                     MeleeSize=10,
                                     ParticipantsScores=db.Blob(
                                         zlib.compress(pickle.dumps([]))))
            self.response.out.write("CREATED NEW GAME TYPE " + rumble + "\n")

            logging.info("Created new game type: " + rumble)
        else:
            field = game.Field == results["field"]
            rounds = (game.Rounds == int(results["rounds"]))
            teams = game.Teams == bool(results["teams"] == "YES")
            melee = game.Melee == bool(results["melee"] == "YES")
            allowed = field and rounds and teams and melee
            if not allowed:
                errstr = "OK. ERROR. Incorrect " + rumble + " config: "
                errorReasons = []
                if not field:
                    errorReasons.append("field size ")
                if not rounds:
                    errorReasons.append("number of rounds ")
                if not teams:
                    errorReasons.append("teams ")
                if not melee:
                    errorReasons.append("melee ")
                logging.error(errstr + string.join(errorReasons, ", ") +
                              "  User: "******"user"])

                return
        scores = None

        try:
            scores = pickle.loads(zlib.decompress(game.ParticipantsScores))
            game.ParticipantsScores = None
            if len(scores) == 0:
                scores = {}
        except:
            scoresdicts = marshal.loads(
                zlib.decompress(game.ParticipantsScores))
            game.ParticipantsScores = None
            scoreslist = [structures.LiteBot(loadDict=d) for d in scoresdicts]
            #for s,d in zip(scoreslist,scoresdicts):
            #    s.__dict__.update(d)
            scores = {s.Name: s for s in scoreslist}
            if len(scores) == 0:
                scores = {}

            #logging.debug("uncompressed scores: " + str(len(s)) + "   compressed: " + str(a))

        for i in [0, 1]:

            if not bots[i].Active or bots[i].Name not in scores:
                bots[i].Active = True
                scores[bots[i].Name] = structures.LiteBot(bots[i])
                newBot = True
                self.response.out.write("Added " + bd[i][0] + " to " + rumble +
                                        "\n")
                logging.info("added new bot!")

        #retrievetime = time.time() - starttime

        scorea = float(results["fscore"])
        scoreb = float(results["sscore"])

        if scorea + scoreb > 0:
            APSa = 100 * scorea / (scorea + scoreb)
        else:
            APSa = 50  #register a 0/0 as 50%
        #APSb = 100 - APSa

        survivala = float(results["fsurvival"])
        survivalb = float(results["ssurvival"])

        survivala = 100.0 * survivala / game.Rounds
        survivalb = 100.0 * survivalb / game.Rounds

        for b, pairings in zip(bots, pairingsarray):

            #b.PairingsList = zlib.compress(marshal.dumps([s.__dict__ for s in pairings]),1)

            if len(pairings) > 0:
                removes = []
                for p in pairings:
                    try:
                        p.APS = float(p.APS)
                        p.Survival = float(p.Survival)
                        p.Battles = int(p.Battles)
                    except:
                        removes.append(pairings.index(p))
                        continue

                removes.sort(reverse=True)
                for i in removes:
                    pairings.pop(i)

        apair = None
        for p in pairingsarray[0]:
            if p.Name == botb:
                apair = p
        if apair is None:
            apair = structures.ScoreSet(name=botb)
            pairingsarray[0].append(apair)

        bpair = None
        for p in pairingsarray[1]:
            if p.Name == bota:
                bpair = p
        if bpair is None:
            bpair = structures.ScoreSet(name=bota)
            pairingsarray[1].append(bpair)

        #participantsSet = set(game.Participants)

        for b, pairings in zip(bots, pairingsarray):
            i = 0
            while i < len(pairings):
                if pairings[i].Name == b.Name:
                    pairings.pop(i)
                    continue
                if not hasattr(pairings[i], "Alive"):
                    pairings[i].Alive = True

                if pairings[i].Alive and pairings[i].Name not in scores:
                    pairings[i].Alive = False

                i += 1
            #b.Pairings = i

        aBattles = apair.Battles

        #rolling average with a half-life of 10k
        maxPerPair = 10000 / len(bots)
        if aBattles > maxPerPair:
            aBattles = maxPerPair

        #bBattles = bpair.Battles

        inv_ab = 1.0 / (aBattles + 1.0)

        apair.APS *= float(aBattles) * inv_ab
        apair.APS += APSa * inv_ab
        apair.__dict__["Min_APS"] = min(APSa,
                                        apair.__dict__.get("Min_APS", 100))
        #bpair.APS *= float(bBattles)/(bBattles + 1)
        #bpair.APS += APSb/(bBattles+1)
        bpair.APS = 100 - apair.APS
        bpair.__dict__["Min_APS"] = min(100 - APSa,
                                        bpair.__dict__.get("Min_APS", 100))

        apair.Survival *= float(aBattles) * inv_ab
        apair.Survival += survivala * inv_ab

        bpair.Survival *= float(aBattles) * inv_ab
        bpair.Survival += survivalb * inv_ab

        apair.Battles += 1
        bpair.Battles = apair.Battles

        apair.LastUpload = datetime.datetime.now().strftime(
            "%Y-%m-%d %H:%M:%S")
        bpair.LastUpload = apair.LastUpload

        for b, pairings in zip(bots, pairingsarray):
            aps = 0.0
            survival = 0.0
            pl = 0
            battles = 0
            alivePairings = 0
            if len(pairings) > 0:

                for p in pairings:
                    if p.Alive:
                        aps += p.APS
                        survival += p.Survival
                        if p.APS > 50:
                            pl += 1
                        else:
                            pl -= 1

                        battles += p.Battles
                        alivePairings += 1

                aps /= alivePairings
                survival /= alivePairings
                b.APS = aps
                b.Survival = survival
                b.PL = pl
                b.Battles = battles

            b.PairingsList = db.Blob(
                zlib.compress(pickle.dumps(pairings, pickle.HIGHEST_PROTOCOL),
                              1))
            b.LastUpload = apair.LastUpload
            b.Pairings = alivePairings

        game.TotalUploads += 1

        #self.response.out.write("<" + str(bots[0].Battles) + " " + str(bots[1].Battles) + ">")
        game.LastUpload = apair.LastUpload
        game.AvgBattles = game.AvgBattles * 0.99 + 0.005 * (bots[0].Battles +
                                                            bots[1].Battles)
        if game.Uploaders is None:
            uploaders = None
        else:
            uploaders = pickle.loads(zlib.decompress(game.Uploaders))
        if uploaders is None or len(uploaders) == 0:
            uploaders = {}
        uploaderName = results["user"]

        try:
            uploader = uploaders[uploaderName]
            uploader.latest = apair.LastUpload
            uploader.total += 1
        except KeyError:
            uploader = structures.User(name=uploaderName)
            uploaders[uploaderName] = uploader
        game.Uploaders = zlib.compress(pickle.dumps(uploaders, -1), 1)

        for b in bots:
            try:
                bUploaders = b.Uploaders
                if uploaderName not in bUploaders:
                    bUploaders.append(uploaderName)
            except:
                b.__dict__["Uploaders"] = [uploaderName]

        with sync_lock:
            for b in bots:
                key = None
                if isinstance(b, structures.BotEntry):
                    key = b.key().name()
                else:
                    key = b.key_name
                botsync[key] = botsync.get(key, 0) + 1

            minSize = min(10, len(scores) / 2)
            wrote = False

            logging.debug("botsync: " + str(len(botsync)))

            if len(botsync) > minSize:
                syncset = botsync.keys()

                syncbotsDict = memcache.get_multi(syncset)

                syncbots = []
                for sb in syncset:
                    b = syncbotsDict.get(sb, None)

                    if b is None or b.PairingsList is None:
                        syncbotsDict.pop(sb, 1)
                    else:
                        syncbots.append(b)

                    botsync.pop(sb, 1)

                try:
                    thisput = []
                    while len(syncbots) > 0:
                        b = syncbots.pop()

                        key = None
                        if isinstance(b, structures.BotEntry):
                            key = b.key().name()
                        else:
                            key = b.key_name

                        putb = structures.BotEntry(key_name=key)
                        putb.init_from_cache(b)
                        thisput.append(putb)

                    db.put(thisput)

                    logging.info("wrote " + str(len(thisput)) +
                                 " results to database")
                    for b in thisput:
                        s = b.key().name()
                        botsync.pop(s, 1)
                        syncbotsDict.pop(s, 1)
                    wrote = True

                except Exception, e:
                    logging.error('Failed to write data: ' + str(e))
def removeFromRumble(self, requests):
    #global global_dict
    global_dict = {}
    if "version" not in requests or requests["version"] is not "1":
        return "ERROR. bad/no version"

    game = requests.get("game", None)
    if game is None:
        return "ERROR. no game specified"

    name = requests.get("name", None)
    if name is None:
        return "ERROR. no name specified"
    while name.count("%20") > 0:
        name = rreplace(name, "%20", " ", 1)

    rumble = global_dict.get(game, None)
    if rumble is None:
        rumble = memcache.get(game)
        if rumble is None:
            rumble = structures.Rumble.get_by_key_name(game)

    entry = None
    if name.count(" ") == 0:
        num_underscores = name.count("_")
        error_messages = []
        for n in range(num_underscores):
            check_name = nth_repl(name, "_", " ", n + 1)

            keyhash = check_name + "|" + game
            entry = global_dict.get(keyhash, None)
            if entry is None:
                entry = memcache.get(keyhash)
                if entry is None:
                    entry = structures.BotEntry.get_by_key_name(keyhash)
                    if entry is None:
                        error_messages.append(
                            "ERROR. name/game does not exist: " + check_name +
                            "/" + game)
                    else:
                        entry = structures.CachedBotEntry(entry)

            if entry is not None:
                name = check_name
                break
        if entry is None:
            return "\n".join(error_messages)
    else:
        return "ERROR. Bot name missing version."

    if isinstance(entry, structures.BotEntry):
        entry = structures.CachedBotEntry(entry)

    global_dict.pop(keyhash, 0)
    memcache.delete(keyhash)

    entry.Active = False

    try:
        scores = pickle.loads(zlib.decompress(rumble.ParticipantsScores))
    except:
        scoresdicts = marshal.loads(zlib.decompress(rumble.ParticipantsScores))
        scoreslist = [structures.LiteBot() for _ in scoresdicts]
        for s, d in zip(scoreslist, scoresdicts):
            s.__dict__.update(d)
        scores = {s.Name: s for s in scoreslist}

    scores.pop(name, 1)
    rumble.ParticipantsScores = zlib.compress(
        pickle.dumps(scores, pickle.HIGHEST_PROTOCOL), 4)

    memcache.delete("home")
    global_dict.pop("home", 0)

    memcache.set(entry.key_name, entry)
    global_dict[entry.key_name] = entry
    modelBot = structures.BotEntry(key_name=entry.key_name)
    modelBot.init_from_cache(entry)
    modelBot.put()

    global_dict[game] = rumble
    memcache.set(game, rumble)
    rumble.put()

    return "OK. " + name + " retired from " + game
Example #3
0
    def post(self):
        try:
            #global global_dict
            #global_dict = {}
            starttime = time.time()
            cutoff_date = datetime.datetime.now() + datetime.timedelta(-365)
            cutoff_date_string = cutoff_date.strftime("%Y-%m-%d %H:%M:%S")

            parts = self.request.body.split("&")
            requests = {}

            if parts is not None and parts[0] != "":
                for pair in parts:
                    ab = pair.split('=')
                    requests[ab[0]] = ab[1]

            force = bool(requests.get("force", False))
            write = bool(requests.get("write", False))
            minwrite = bool(requests.get("minwrite", False))

            rpcList = []
            client = memcache.Client()

            q = structures.Rumble.all()
            rumbles = []
            for r in q.run():
                memr = memcache.get(r.Name)
                if memr is not None:
                    r = memr
                if r.BatchScoresAccurate and not force:
                    continue
                rumbles.append(r)

            for r in rumbles:
                scoresdicts = pickle.loads(
                    zlib.decompress(r.ParticipantsScores))
                entries = len(scoresdicts)
                r.__dict__["entries"] = entries
            rumbles.sort(key=lambda r: -r.__dict__["entries"])

            first = True
            for r in rumbles:
                if not first:
                    time.sleep(5)
                    gc.collect()
                    gc.collect(2)
                first = False

                logging.info("mem usage at start of " + r.Name + ": " +
                             str(runtime.memory_usage().current()) + "MB")
                try:
                    scores = pickle.loads(zlib.decompress(
                        r.ParticipantsScores))
                except:
                    scoresdicts = marshal.loads(
                        zlib.decompress(r.ParticipantsScores))
                    scoreslist = [structures.LiteBot() for _ in scoresdicts]
                    for s, d in zip(scoreslist, scoresdicts):
                        s.__dict__.update(d)
                    scores = {s.Name: s for s in scoreslist}

                if len(scores) == 0:
                    continue

                r.ParticipantsScores = None
                #gc.collect()

                particHash = [p + "|" + r.Name for p in scores]

                particSplit = list_split(particHash, 32)
                ppDict = {}
                for l in particSplit:
                    ppDict.update(memcache.get_multi(l))
                    time.sleep(0.1)

                particSplit = None

                bots = [ppDict.get(h, None) for h in particHash]

                botsdict = {}

                missingHashes = []
                missingIndexes = []
                for i in xrange(len(bots)):
                    if bots[i] is None:
                        missingHashes.append(particHash[i])
                        missingIndexes.append(i)

                    elif isinstance(bots[i], structures.BotEntry):
                        bots[i] = structures.CachedBotEntry(bots[i])

                if len(missingHashes) > 0:
                    bmis = structures.BotEntry.get_by_key_name(missingHashes)

                    #lost = False
                    lostList = []

                    for i in xrange(len(missingHashes)):
                        if bmis[i] is not None:
                            cb = structures.CachedBotEntry(bmis[i])
                            bots[missingIndexes[i]] = cb
                            botsdict[missingHashes[i]] = cb

                        else:
                            bots[missingIndexes[i]] = None
                            lostList.append(missingHashes[i])
                            #lost = True

                while len(particHash) > 0:
                    particHash.pop()
                particHash = None

                while len(missingHashes) > 0:
                    missingHashes.pop()
                missingHashes = None

                while len(missingIndexes) > 0:
                    missingIndexes.pop()
                missingIndexes = None

                logging.info("mem usage after loading bots: " +
                             str(runtime.memory_usage().current()) + "MB")

                bots = filter(lambda b: b is not None, bots)

                get_key = attrgetter("APS")
                bots.sort(key=lambda b: get_key(b), reverse=True)

                gc.collect()

                botIndexes = {}
                for i, b in enumerate(bots):
                    b.Name = b.Name.encode('ascii')
                    intern(b.Name)
                    botIndexes[b.Name] = i
                    b.VoteScore = 0.

                botlen = len(bots)
                APSs = numpy.empty([botlen, botlen])
                APSs.fill(numpy.nan)
                totalAlivePairs = 0
                for i, b in enumerate(bots):
                    try:
                        pairings = pickle.loads(zlib.decompress(
                            b.PairingsList))
                    except:
                        pairsDicts = marshal.loads(
                            zlib.decompress(b.PairingsList))

                        pairings = [structures.ScoreSet() for _ in pairsDicts]
                        for s, d in zip(pairings, pairsDicts):
                            s.__dict__.update(d)
                    removes = []
                    alivePairings = 0
                    for q, p in enumerate(pairings):
                        j = botIndexes.get(p.Name, -1)
                        if j != -1:
                            APSs[j, i] = numpy.float64(p.APS)
                            p.Alive = True
                            alivePairings += 1
                        else:
                            removes.append(q)
                    b.Pairings = alivePairings
                    totalAlivePairs += alivePairings
                    removes.reverse()
                    removed = False
                    for q in removes:
                        p = pairings[q]
                        if p.LastUpload < cutoff_date_string:
                            removed = True
                            pairings.pop(q)
                        else:
                            if p.Alive:
                                removed = True
                            p.Alive = False
                    if removed:
                        b.PairingsList = zlib.compress(
                            pickle.dumps(pairings, -1), 1)

                gc.collect()

                APSs += numpy.float64(100) - APSs.transpose()
                APSs *= numpy.float64(0.5)

                numpy.fill_diagonal(APSs, numpy.nan)

                gc.collect()
                logging.info(
                    str(len(bots)) + " bots loaded, total of " +
                    str(totalAlivePairs) + " alive pairings")
                logging.info("mem usage after unzipping pairings: " +
                             str(runtime.memory_usage().current()) + "MB")

                #Vote
                mins = numpy.nanmax(APSs, 1)
                for i, minimum in enumerate(mins):
                    minIndexes = numpy.argwhere(APSs[i, ...] == minimum)
                    ties = len(minIndexes)
                    if ties > 0:
                        increment = 1. / ties
                        for minIndex in minIndexes:
                            bots[minIndex].VoteScore += increment

                #inv_len = 1.0/botlen
                for b in bots:
                    if b.Pairings > 0:
                        b.VoteScore = 100.0 * b.VoteScore / float(b.Pairings)
                    else:
                        b.VoteScore = 0

                #KNN PBI
                half_k = int(math.ceil(math.sqrt(botlen) / 2))
                KNN_PBI = -numpy.ones((botlen, botlen))
                for i in xrange(len(bots)):
                    low_bound = max([0, i - half_k])
                    high_bound = min([botlen - 1, i + half_k])
                    low_high_bound = min([i + 1, high_bound])
                    before = APSs[:, low_bound:i]
                    after = APSs[:, low_high_bound:high_bound]
                    compare = numpy.hstack((before, after))
                    mm = numpy.mean(numpy.ma.masked_array(
                        compare, numpy.isnan(compare)),
                                    axis=1)
                    KNN_PBI[:, i] = APSs[:, i] - mm.filled(numpy.nan)

    #                a[i] = 0
    #               logging.info("mean error of transpose: " + str(numpy.mean(numpy.square(a))))

    #KNN_PBI[KNN_PBI == numpy.nan] = -1

    #logging.info("mem usage after KNNPBI: " + str(runtime.memory_usage().current()) + "MB")
    # Avg Normalised Pairing Percentage

                mins = numpy.nanmin(APSs, 1)
                maxs = numpy.nanmax(APSs, 1)
                inv_ranges = numpy.float64(1.0) / (maxs - mins)
                NPPs = -numpy.ones((botlen, botlen))
                for i in range(botlen):
                    if numpy.isfinite(inv_ranges[i]):
                        NPPs[i, :] = numpy.float64(100) * (
                            APSs[i, :] - mins[i]) * inv_ranges[i]
                    else:
                        NPPs[i, :] = numpy.float64(100)

                #NPPs[NPPs] = -1

                #logging.info("mem usage after ANPP: " + str(runtime.memory_usage().current()) + "MB")

                changedBots = []  #bots with new pairings since last run

                # save to cache
                botsdict = {}

                for i, b in enumerate(bots):
                    #                try:
                    pairings = pickle.loads(zlib.decompress(b.PairingsList))
                    #                except:
                    #                    pairsDicts = marshal.loads(zlib.decompress(b.PairingsList))
                    #
                    #                    pairings = [structures.ScoreSet() for _ in pairsDicts]
                    #                    for s,d in zip(pairings,pairsDicts):
                    #                        s.__dict__.update(d)
                    nppCount = 0
                    totalNPP = 0.0

                    apsCount = 0
                    totalAPS = 0.0

                    aliveCount = 0

                    changed = False
                    for p in pairings:
                        j = botIndexes.get(p.Name, -1)
                        if j != -1:
                            p.Alive = True
                            changePotential = (p.KNNPBI == 0.0 and p.NPP == -1)

                            aliveCount += 1
                            p.KNNPBI = float(KNN_PBI[j, i])
                            p.NPP = float(NPPs[j, i])

                            if not numpy.isnan(APSs[j, i]):
                                p.APS = float(APSs[j, i])
                                totalAPS += p.APS
                                apsCount += 1

                            if numpy.isnan(p.KNNPBI):
                                p.KNNPBI = 0

                            if numpy.isnan(p.NPP):
                                p.NPP = -1
                            else:
                                totalNPP += p.NPP
                                nppCount += 1

                            if changePotential and p.KNNPBI != 0.0 and p.NPP != -1:
                                changed = True
                        else:
                            p.Alive = False
                            p.KNNPBI = 0
                            p.NPP = -1

                    if nppCount > 0:
                        b.ANPP = float(totalNPP / nppCount)
                    else:
                        b.ANPP = -1.0
                    if apsCount > 0:
                        b.APS = float(totalAPS / apsCount)
                    else:
                        b.APS = -1.0

                    b.PairingsList = zlib.compress(pickle.dumps(pairings, -1),
                                                   1)
                    b.Pairings = aliveCount
                    if b.Pairings > 0:
                        botsdict[b.key_name] = b
                    if changed:
                        changedBots.append(b)

                KNN_PBI = None
                APSs = None
                NPPs = None
                logging.info("mem usage after zipping: " +
                             str(runtime.memory_usage().current()) + "MB")

                gc.collect()
                #logging.info("mem usage after gc: " + str(runtime.memory_usage().current()) + "MB")
                if len(botsdict) > 0:
                    splitlist = dict_split(botsdict, 20)
                    logging.info("split bots into " + str(len(splitlist)) +
                                 " sections")

                    for d in splitlist:
                        rpcList.append(client.set_multi_async(d))
                        time.sleep(.5)  #throttle

                    logging.info("wrote " + str(len(botsdict)) +
                                 " bots to memcache")

                botsdict.clear()
                botsdict = None

                scores = {b.Name: structures.LiteBot(b) for b in bots}

                # bots = None
                r.ParticipantsScores = None
                gc.collect()

                r.ParticipantsScores = db.Blob(
                    zlib.compress(
                        pickle.dumps(scores, pickle.HIGHEST_PROTOCOL), 3))
                logging.info("mem usage after participants zipping: " +
                             str(runtime.memory_usage().current()) + "MB")
                #r.ParticipantsScores = zlib.compress(marshal.dumps([scores[s].__dict__ for s in scores]),4)
                scores = None

                if write:
                    writebots = [None] * len(bots)
                    for i, b in enumerate(bots):
                        putb = structures.BotEntry(key_name=b.key_name)
                        putb.init_from_cache(b)
                        writebots[i] = putb
                    write_lists = list_split(writebots, 50)
                    for subset in write_lists:
                        db.put(subset)
                        time.sleep(0.1)  #throttle
                    logging.info("wrote " + str(len(writebots)) +
                                 " bots to database")

                while len(bots) > 0:
                    bots.pop()
                bots = None

                if minwrite:
                    writebots = [None] * len(changedBots)
                    for i, b in enumerate(changedBots):
                        putb = structures.BotEntry(key_name=b.key_name)
                        putb.init_from_cache(b)
                        writebots[i] = putb
                    write_lists = list_split(writebots, 50)
                    for subset in write_lists:
                        db.put(subset)
                        time.sleep(0.1)
                    logging.info("wrote " + str(len(writebots)) +
                                 " changed bots to database")

                while len(changedBots) > 0:
                    changedBots.pop()
                changedBots = None
                gc.collect()

                if write or minwrite:
                    r.BatchScoresAccurate = True

                rpcList.append(client.set_multi_async({r.Name: r}))

                db.put([r])
                #gc.collect()
                r = None
                logging.info("mem usage after write: " +
                             str(runtime.memory_usage().current()) + "MB")

            for rpc in rpcList:
                rpc.get_result()

            elapsed = time.time() - starttime
            logging.info("Success in " + str(round(1000 * elapsed) / 1000) +
                         "s")
            self.response.out.write("Success in " +
                                    str(round(1000 * elapsed)) + "ms")
        except:
            logging.exception('')
            elapsed = time.time() - starttime
            logging.info("Error in " + str(round(1000 * elapsed) / 1000) + "s")
            self.response.out.write("Error in " + str(round(1000 * elapsed)) +
                                    "ms")