keeping state on the server side is a common hindrance to scaling out beyond a single server. anything you can have the client keep track of, you do not have to worry about synchronizing across nodes on the server side.

without getting into crazy stuff like ASN.1, wanted to know how much ‘stuff’ you can cram into the web state storage method de jour, the cookie.

our storage algorithm is straightforward python, if extremely naive.

def chksum(string, salt):
        m = hashlib.md5()
        m.update(string)
        m.update(salt)
        return m.hexdigest()

def freeze(obj, salt, level = 1):
        j = json.dumps(obj)
        c = chksum(j,salt)
        payload = "%s:%s" % ( c, j )
        c = zlib.compress(payload, level)
        b = base64.standard_b64encode(c) 
        return b

def thaw(string, salt):
        c = base64.standard_b64decode(string)
        payload = zlib.decompress(c)
        (h, j) = payload.split(":",1)
        c = chksum(j, salt)

        if( h != c):
                raise InvalidChecksum

        obj = json.loads(j)
        return obj

serialize to JSON, compute checksum, smoosh it together, compress, and b64encode.

gotchas:

  • there is no encryption here. the payload can be easily extracted on the client side.
  • the server side salt allows tenuous authentication of the payload, but should not be used for security tokens.
  • the server side salt also prevents cookie state from being generated or modified client side. ergo it is only relative for keeping state with traditional request/response patterns, and not ‘heavy’ client side applications.
  • for production use, a stricter authentication scheme combined with proper encryption channels would be highly suggested.

enough of that, moving on…

the spec states 50 cookies per domain, with a max of 4093 bytes per domain. so, if we set just a single cookie for our state, we roughly have 4000 bytes to work with. how does that crunch out?

./cookiemonster.py -m 4000 -k 4 8 -p 8 32 64 -z 0 1 9
key,val,  max, z,items
  4,  8, 4000, 0, 147
  4,  8, 4000, 1, 237
  4,  8, 4000, 9, 244
  8,  8, 4000, 0, 123
  8,  8, 4000, 1, 187
  8,  8, 4000, 9, 191
  4, 32, 4000, 0,  67
  4, 32, 4000, 1,  92
  4, 32, 4000, 9,  93
  8, 32, 4000, 0,  61
  8, 32, 4000, 1,  84
  8, 32, 4000, 9,  84
  4, 64, 4000, 0,  39
  4, 64, 4000, 1,  51
  4, 64, 4000, 9,  51
  8, 64, 4000, 0,  37
  8, 64, 4000, 1,  48
  8, 64, 4000, 9,  49

if we’re tidy with our naming and storage, can easily cram close 200 small KV pairs into our allocation. if we move up to larger payloads, drops significantly, but still 50 items. not too bad. plenty enough for keeping stuff like cart state around.

how about an Ethernet frame? 1400ish

./cookiemonster.py -m 1400 -k 4 -p 8 32 64 -z 0 9
key,val,  max, z,items
  4,  8, 1400, 0,  50
  4,  8, 1400, 9,  82
  4, 32, 1400, 0,  23
  4, 32, 1400, 9,  31
  4, 64, 1400, 0,  14
  4, 64, 1400, 9,  18

jumbo frame? 8500

./cookiemonster.py -m 8500 -k 4 -p 8 32 64 -z 0 9
key,val,  max, z,items
  4,  8, 8500, 0, 314
  4,  8, 8500, 9, 524
  4, 32, 8500, 0, 143
  4, 32, 8500, 9, 199
  4, 64, 8500, 0,  83
  4, 64, 8500, 9, 110

github