Skip to content

Instantly share code, notes, and snippets.

@sonnyksimon
Created December 4, 2019 15:56
Show Gist options
  • Save sonnyksimon/d03a8eef002e58d22fb4ae58ebe4c12f to your computer and use it in GitHub Desktop.
Save sonnyksimon/d03a8eef002e58d22fb4ae58ebe4c12f to your computer and use it in GitHub Desktop.
reddithing.py
class NotFound(Exception):
pass
CreationError = tdb.CreationError
thing_types = {}
rel_types = {}
class SafeSetAttr:
def __init__(self, cls):
self.cls = cls
def __enter__(self):
self.cls.__safe__ = True
def __exit__(self, type, value, tb):
self.cls.__safe__ = False
class TdbTransactionContext(object):
def __enter__(self):
tdb.transactions.begin()
def __exit__(self, exc_type, exc_value, traceback):
if exc_type:
tdb.transactions.rollback()
raise
else:
tdb.transactions.commit()
class DataThing(object):
_base_props = ()
_int_props = ()
_data_int_props = ()
_int_prop_suffix = None
_defaults = {}
_essentials = ()
c = operators.Slots()
__safe__ = False
_cache = None
_cache_ttl = int(timedelta(hours=12).total_seconds())
def __init__(self):
pass
#TODO some protection here?
def __setattr__(self, attr, val, make_dirty=True):
pass
def __setstate__(self, state):
# pylibmc's automatic unpicking will call __setstate__ if it exists.
# if we don't implement __setstate__ the check for existence will fail
# in an atypical (and not properly handled) way because we override
# __getattr__. the implementation provided here is identical to what
# would happen in the default unimplemented case.
pass
def __getattr__(self, attr):
pass
@classmethod
def _cache_prefix(cls):
pass
def _cache_key(self):
pass
@classmethod
def get_things_from_db(cls, ids):
"""Read props from db and return id->thing dict."""
pass
@classmethod
def get_things_from_cache(cls, ids, stale=False, allow_local=True):
"""Read things from cache and return id->thing dict."""
pass
@classmethod
def write_things_to_cache(cls, things_by_id):
"""Write id->thing dict to cache.
Used to populate the cache after a cache miss/db read. To ensure we
don't clobber a write by another process (we don't have a lock) we use
add_multi to only set the values that don't exist.
"""
pass
def get_read_modify_write_lock(self):
"""Return the lock to be used when doing a read-modify-write.
When modifying a Thing we must read its current version from cache and
update that to avoid clobbering modifications made by other processes
after we first read the Thing.
"""
pass
def write_new_thing_to_db(self):
"""Write the new thing to db and return its id."""
pass
def write_props_to_db(self, props, data_props, brand_new_thing):
"""Write the props to db."""
pass
def write_changes_to_db(self, changes, brand_new_thing=False):
"""Write changes to db."""
pass
def write_thing_to_cache(self, lock, brand_new_thing=False):
"""After modifying a thing write the entire object to cache.
The caller must either pass in the read_modify_write lock or be acting
for a newly created thing (that has therefore never been cached before).
"""
pass
def update_from_cache(self, lock):
"""Read the current value of thing from cache and update self.
To be used before writing cache to avoid clobbering changes made by a
different process. Must be called under write lock.
"""
pass
@classmethod
def record_cache_write(cls, event, delta=1):
pass
def _commit(self):
"""Write changes to db and write the full object to cache.
When writing to postgres we write only the changes. The data in postgres
is the canonical version.
For a few reasons (speed, decreased load on postgres, postgres
replication lag) we want to keep a perfectly consistent copy of the
thing in cache.
To achieve this we read the current value of the thing from cache to
pull in any changes made by other processes, apply our changes to the
thing, and finally set it in cache. This is done under lock to ensure
read/write safety.
If the cached thing is evicted or expires we must read from postgres.
Failure cases:
* Write to cache fails. The cache now contains stale/incorrect data. To
ensure we recover quickly TTLs should be set as low as possible
without overloading postgres.
* There is long replication lag and high cache pressure. When an object
is modified it is written to cache, but quickly evicted, The next
lookup might read from a postgres secondary before the changes have
been replicated there. To protect against this replication lag and
cache pressure should be monitored and kept at acceptable levels.
* Near simultaneous writes that create a logical inconsistency. Say
request 1 and request 2 both read state 0 of a Thing. Request 1
changes Thing.prop from True to False and writes to cache and
postgres. Request 2 examines the value of Thing.prop, sees that it is
True, and due to logic in the app sets Thing.prop_is_true to True and
writes to cache and postgres. Request 2 didn't clobber the change
made by request 1, but it made a logically incorrect change--the
resulting state is Thing.prop = False and Thing.prop_is_true = True.
Logic like this should be identified and avoided wherever possible, or
protected against using locks.
"""
pass
def _incr(self, prop, amt=1):
pass
@property
def _id36(self):
pass
@class_property
def _fullname_prefix(cls):
pass
@classmethod
def _fullname_from_id36(cls, id36):
pass
@property
def _fullname(self):
pass
@classmethod
def _byID(cls, ids, data=True, return_dict=True, stale=False,
ignore_missing=False):
pass
@classmethod
def _byID36(cls, id36s, return_dict = True, **kw):
pass
@classmethod
def _by_fullname(cls, names,
return_dict = True,
ignore_missing=False,
**kw):
pass
@property
def _dirty(self):
pass
@classmethod
def _query(cls, *a, **kw):
pass
class ThingMeta(type):
pass
class Thing(DataThing):
@classmethod
def record_cache_write(cls, event, delta=1): pass
@classmethod
def get_things_from_db(cls, ids):
"""Read props from db and return id->thing dict."""
pass
def write_new_thing_to_db(self):
"""Write the new thing to db and return its id."""
pass
def write_new_thing_to_db(self):
"""Write the new thing to db and return its id."""
pass
def write_props_to_db(self, props, data_props, brand_new_thing):
"""Write the props to db."""
pass
def _incr(self, prop, amt=1):
"""Increment self.prop."""
pass
@property
def _age(self): pass
@property
def _hot(self): pass
@property
def _score(self): pass
@property
def _controversy(self): pass
@property
def _confidence(self): pass
@property
def num_votes(self): pass
@property
def is_distinguished(self):
"""Return whether this Thing has a special flag on it (mod, admin, etc).
Done this way because distinguish is implemented in such a way where it
does not exist by default, but can also be set to a string of 'no',
which also means it is not distinguished.
"""
pass
@classmethod
def _query(cls, *all_rules, **kw): pass
@classmethod
def sort_ids_by_data_value(cls, thing_ids, value_name,
limit=None, desc=False): pass
def update_search_index(self, boost_only=False): pass
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment