Created
December 4, 2019 15:56
-
-
Save sonnyksimon/d03a8eef002e58d22fb4ae58ebe4c12f to your computer and use it in GitHub Desktop.
reddithing.py
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
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