{ "info": { "author": "Michael J Brams", "author_email": "mjbrams@3top.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "Framework :: Django :: 1.6", "Framework :: Django :: 1.7", "Framework :: Django :: 1.8", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "=========\r\nlru2cache\r\n=========\r\n\r\n.. image:: https://travis-ci.org/3Top/lru2cache.svg?branch=master\r\n :target: https://travis-ci.org/3Top/lru2cache\r\n :alt: Travis-CI\r\n\r\n.. image:: https://codeclimate.com/github/3Top/lru2layer/badges/gpa.svg\r\n :target: https://codeclimate.com/github/3Top/lru2layer\r\n :alt: Code Climate\r\n\r\n\r\n.. image:: https://coveralls.io/repos/3Top/lru2cache/badge.svg?branch=master&service=github\r\n :target: https://coveralls.io/github/3Top/lru2cache?branch=master\r\n :alt: Coveralls.io\r\n\r\n\r\nA `least recently used (LRU) `_\r\n2 layer caching mechanism based in part on the Python 2.7 back-port of\r\n``functools.lru_cache``\r\n\r\nThis was developed by `3Top, Inc. `_ for use with\r\nour ranking and recommendation platform, http://www.3top.com.\r\n\r\nlru2cache is a decorator that can be used with any user function or method to\r\ncache the most recent results in a local cache and using the django cache\r\nframework to cache results in a shared cache.\r\n\r\nThe first layer of caching is stored in a callable that wraps the function or\r\nmethod. As with 'functools.lru_cache' a dict is used to store the cached\r\nresults, therefore positional and keyword arguments must be hashable. Each\r\ninstance stores up to ``l1_maxsize`` results that vary on the arguments. The\r\ndiscarding of the LRU cached values is handled by the decorator.\r\n\r\nThe second layer of caching requires a shared cache that can make use of\r\nDjango's cache framework. In this case it is assumed that any LRU mechanism\r\nis handled by the shared cache backend.\r\n\r\nThis arrangement allows a process that accesses a function multiple times to\r\nretrieve the value without the expense of requesting it from a shared cache,\r\nwhile still allowing different processes to access the result from the shared\r\ncache.\r\n\r\nInstallation & Configuration\r\n============================\r\nThe easiest and best way to install this is with pip::\r\n\r\n pip install lru2cache\r\n\r\nIf available this package will use SpookyHash V2 as a hashing mechanism.\r\nSpooky is a good fast hashing algorithm that should be suitable for most uses.\r\nIf it is not available the package will fall back to SHA-256 from the standard\r\nhashlib. Because SHA-256 is a proper cryptographic hash it requires more\r\ncomputation than Spooky. To install spooky use pip::\r\n\r\n pip install spooky 2\r\n\r\nOnce lru2cache is installed you will need to configure a shared cache as an\r\nl2 cache. If you are using Django your settings file will contain something\r\nsimilar to the following in the settings file::\r\n\r\n CACHES = {\r\n 'default': {\r\n 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',\r\n 'LOCATION': '127.0.0.1:11211',\r\n 'TIMEOUT': 1200,\r\n },\r\n 'l2cache': {\r\n 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',\r\n 'LOCATION': '127.0.0.1:11211',\r\n 'TIMEOUT': None,\r\n },\r\n }\r\n\r\nIf you do not want to use either ``default`` or ``l2cache`` you will need to\r\nspecify the name of the cache.\r\n\r\nBenefits Over functools.lru_cache\r\n=================================\r\n\r\n**Local and Shared Cache** - Combining both types of cache is much more\r\neffective than either used on it's own. The local cache eliminates the\r\nlatency of calls to a shared cache, while the shared cache eliminates\r\nthe expense of returning the result\r\n\r\n**The Ability to Not Cache None Results** - This may seem like a minor thing\r\nbut in our environment it has greatly reduced the frequency of cache\r\ninvalidations.\r\n\r\nUsage\r\n=====\r\n::\r\n\r\n @utils.lru2cache(l1_maxsize=128, none_cache=False, typed=False, l2cache_name='l2cache', inst_attr='id')\r\n\r\nUsage is as simple as adding the decorator to a function or method as seen in\r\nthe below examples from our test cases::\r\n\r\n from lru2cache import utils\r\n\r\n @utils.lru2cache()\r\n def py_cached_func(x, y):\r\n return 3 * x + y\r\n\r\n\r\n class TestLRUPy(TestLRU):\r\n module = utils\r\n cached_func = py_cached_func,\r\n\r\n @utils.lru2cache()\r\n def cached_meth(self, x, y):\r\n return 3 * x + y\r\n\r\n @staticmethod\r\n @utils.lru2cache()\r\n def cached_staticmeth(x, y):\r\n return 3 * x + y\r\n\r\nIf ``l1_maxsize`` is set to ``None``, the LRU feature is disabled and the L1 cache\r\ncan grow without bound. The LRU feature performs best when maxsize is a power-of-two.\r\n\r\nif ``none_cache`` is ``True`` than ``None`` results will be cached, otherwise they\r\nwill not.\r\n\r\nIf ``typed`` is set to ``True``, function arguments of different types will be\r\ncached separately. For example, f(3) and f(3.0) will be treated as distinct\r\ncalls with distinct results.\r\n\r\nIf ``l2cache_name`` is specified it will be used as the shared cache. Otherwise\r\nit will attempt to use a cache named ``l2cache`` and if not found fall back to\r\n``default``.\r\n\r\n``inst_attr`` is the attribute used to uniquely identify an object when wrapping\r\na method. In Django this will typically be ``id`` however if it is not you will\r\nneed to specify what attribute should be used.\r\n\r\nCache Management\r\n================\r\nSince the lru2cache decorator does not provide a timeout for its cache although\r\nit provides other mechanisms for programatically managing the cache.\r\n\r\nCache Statistics\r\n----------------\r\nAs with lru_cache, one can view the cache statistics via a named tuple\r\n(l1_hits, l1_misses, l2_hits, l2_misses, l1_maxsize, l1_currsize), with\r\n``f.cache_info()``. These stats are stored within an instance, and therefore\r\nare specific to that instance. Cumulative statistics for the shared cache would\r\nneed to be obtained from the shared cache.\r\n\r\nClearing Instance Cache\r\n-----------------------\r\nthe cache and statistics associated with a function or method can be cleared with::\r\n\r\n f.cache_clear()\r\n\r\nClearing Shared Cache\r\n---------------------\r\nA shared cache can easily be cleared with the following::\r\n\r\n from django.core import cache\r\n\r\n lru2cache_cache = cache.get_cache('l2cache')\r\n lru2cache_cache.clear()\r\n\r\n\r\nInvalidating Cached Results\r\n---------------------------\r\nTo invalidate the cache for a specific set of arguments, including the instance\r\none can pass the same arguments to invalidate the both L1 and L2 caches::\r\n\r\n f.invalidate(*args, **kwargs)\r\n\r\nin the case of a method you do need to explicitly pass the instance as in the\r\nfollowing::\r\n\r\n foo.f.invalidate(foo, a, b)\r\n\r\nRefreshing the Cache\r\n--------------------\r\nThis is not yet implemented as a function but can be accomplished by first calling\r\ninvalidate and then calling the function\r\n\r\nAccessing the Function without Cache\r\n------------------------------------\r\nThe un-cached underlying function can always be accessed with ``f.__wrapped__``.\r\n\r\nBackground and Development\r\n--------------------------\r\nAt `3Top `_ We needed a way to improve performance of\r\nslow queries, not just those using the Django ORM, but also for queries to\r\nother data stores and services. We started off with a simpler centralized\r\ncaching solution using Memcached, but even those queries, when called frequently,\r\ncan start to cause delays. Therefore we sought a means of caching at two layers.\r\n\r\nInitially we looked at the possibility of using two different mechanisms but\r\nwe quickly saw the advantage of maintaining the same set of keys for both\r\ncaches and decided to create our own mechanism.\r\n\r\nWe used a backport python 3 ``functools.lru_cache()`` decorator as a starting\r\npoint for developing an in instance cache with LRU capabilities. However we\r\nneeded to ensure the keys would also be unique enough to use with a shared\r\ncache. We leverage Django's excellent cache framework for managing the layer 2\r\ncache. This allows the use of any shared cache supported by Django.\r\n\r\nTests\r\n-----\r\nAs a starting point I incorporated most of the tests for\r\n``functools.lru_cache()`` with minor changes to make them work with python 2.7\r\nand incorporated the l2_cache stats. We will continue to add tests to validate\r\nthe additional functionality provided by this decorator.", "description_content_type": null, "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/3Top/lru2cache", "keywords": "cache caching memoize lru layered layer2 l1 l2 decorator fifo", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "lru2cache", "package_url": "https://pypi.org/project/lru2cache/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/lru2cache/", "project_urls": { "Homepage": "https://github.com/3Top/lru2cache" }, "release_url": "https://pypi.org/project/lru2cache/0.1.2/", "requires_dist": [ "coverage; extra == 'test'" ], "requires_python": "", "summary": "A least recently used (LRU) 2 layer caching mechanism based in part on the Python 2.7 back-port of lru_cache", "version": "0.1.2" }, "last_serial": 1953802, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "d2015d10e6062cf2955fd8237d479c13", "sha256": "0acb81130b6a1c9f1c1a1cefbf1e6a7381c56509f667eef3a22c363f4e097cfc" }, "downloads": -1, "filename": "lru2cache-0.1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "d2015d10e6062cf2955fd8237d479c13", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 11703, "upload_time": "2016-01-12T16:40:27", "url": "https://files.pythonhosted.org/packages/b2/d8/d0f1d71a8e5ac41ef91493dad64f0013ec348eb9f789a27bd9913289c814/lru2cache-0.1.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "a953736c41c1ec7a7018e2dded630c10", "sha256": "2669156f40c070b2608eacad74eebd482dfe616b9adc6cd728e7a9a633ebee79" }, "downloads": -1, "filename": "lru2cache-0.1.0.tar.gz", "has_sig": false, "md5_digest": "a953736c41c1ec7a7018e2dded630c10", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8962, "upload_time": "2016-01-12T16:40:34", "url": "https://files.pythonhosted.org/packages/73/f9/288b0eeb359be3052fc91d71ed38ba8a8217afafbf6d7ecc352627125aab/lru2cache-0.1.0.tar.gz" } ], "0.1.1": [ { "comment_text": "", "digests": { "md5": "c6667cb733cc238c402b93c84a720e26", "sha256": "8dd99ec83f41b407e41efe4404b0667695fc265e13535519e44a94ca0a1fcc1c" }, "downloads": -1, "filename": "lru2cache-0.1.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "c6667cb733cc238c402b93c84a720e26", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 11539, "upload_time": "2016-02-03T22:49:47", "url": "https://files.pythonhosted.org/packages/74/b2/3bff9f27a72a00a00fd1670907c1cbc2e866407586bdd759e61fdbc4c265/lru2cache-0.1.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "d1b4978b061e3cb4bc54c09f3d673e0a", "sha256": "1b4ab56e607a1e9b91d3cea555cb47f856e8f6f45c0e0562cf4c61fa60707e8c" }, "downloads": -1, "filename": "lru2cache-0.1.1.tar.gz", "has_sig": false, "md5_digest": "d1b4978b061e3cb4bc54c09f3d673e0a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8749, "upload_time": "2016-02-03T22:50:02", "url": "https://files.pythonhosted.org/packages/b0/3c/0312822ea517def82a41dcd2645918aa06b91d4a69f4ab45871e63342e34/lru2cache-0.1.1.tar.gz" } ], "0.1.2": [ { "comment_text": "", "digests": { "md5": "9a20fcefefd92f43e241a451d9f00fe4", "sha256": "344c910b5411bcf96538953c0f9b3c880dba0186edd0c56b53dc903d8d0849bb" }, "downloads": -1, "filename": "lru2cache-0.1.2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "9a20fcefefd92f43e241a451d9f00fe4", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 13659, "upload_time": "2016-02-05T17:40:57", "url": "https://files.pythonhosted.org/packages/87/92/7d0260cd016980d0ec026cd944fee5eecf517ab9913e18df0701e0e90605/lru2cache-0.1.2-py2.py3-none-any.whl" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "9a20fcefefd92f43e241a451d9f00fe4", "sha256": "344c910b5411bcf96538953c0f9b3c880dba0186edd0c56b53dc903d8d0849bb" }, "downloads": -1, "filename": "lru2cache-0.1.2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "9a20fcefefd92f43e241a451d9f00fe4", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 13659, "upload_time": "2016-02-05T17:40:57", "url": "https://files.pythonhosted.org/packages/87/92/7d0260cd016980d0ec026cd944fee5eecf517ab9913e18df0701e0e90605/lru2cache-0.1.2-py2.py3-none-any.whl" } ] }