{ "info": { "author": "John Carlyle", "author_email": "", "bugtrack_url": null, "classifiers": [ "Development Status :: 2 - Pre-Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Natural Language :: English", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6" ], "description": "Lynk\n====\n\n|build status| |codecov| |doc status| |version| |license|\n\n.. |build status| image:: https://travis-ci.com/stealthycoin/lynk.svg?branch=master\n :target: https://travis-ci.com/stealthycoin/lynk\n :alt: Build Status\n.. |doc status| image:: https://readthedocs.org/projects/lynk/badge/?version=latest\n :target: https://lynk.readthedocs.io/en/latest/\n :alt: Doc Status\n.. |version| image:: http://img.shields.io/pypi/v/lynk.svg?style=flat\n :target: https://pypi.org/project/lynk/\n :alt: Version\n.. |license| image:: https://img.shields.io/badge/license-Apache%202-blue.svg\n :target: https://github.com/stealthycoin/lynk/blob/master/LICENSE\n :alt: License\n.. |codecov| image:: https://codecov.io/gh/stealthycoin/lynk/branch/master/graph/badge.svg\n :target: https://codecov.io/gh/stealthycoin/lynk\n\n\n.. intro-begin\n\nLynk is a Distributed Lock Manager (DLM) that uses DynamoDB to track the state\nof its locks. Lynk is a cooperative locking scheme where each client assumes\nthat all others in the system are obeying a set of rules in order to assure\nthe integrity of the locks.\n\n.. intro-end\n\n\nDocumentation\n=============\n\nThe docs are hosted at `readthedocs `_\n\n\nQuickstart\n==========\n\nThe quickstart guide looks better at\n`readthedocs `_\n\n.. quick-start-begin\n\n\nInstallation\n------------\n\nLynk is available on PyPi as ``lynk`` and can be installed in the usual way\nwith pip::\n\n $ pip install lynk\n\n\nAWS Credentials\n---------------\n\nLynk uses boto3 in order to make all calls to AWS, which means it uses the\nboto3 standard credential chain. Make sure your machine has AWS credentials\nconfigured in the way `boto3 expects `_.\n\n\nCreating a table\n----------------\n\nIn order to store the locks we need to create a DynamoDB table. For ease of\ngetting started there is a command line tool installed along with the package\nto help manage lynk tables.\n\nTo create a table called ``quickstart`` run the ``lynk create-table`` command::\n\n $ lynk create-table lynk-quickstart\n Creating table lynk-quickstart\n Created\n\nWith the ``lynk list-tables`` command line tool you can check a list of tables\ncreated this way by lynk::\n\n $ lynk list-tables\n lynk-quickstart\n\n\nCreating a lock\n---------------\n\nLocks are shared through a DynamoDB table, in our case we will be using the\n``lynk-quickstart`` we created earlier table. Locks are distinguished by a\nlock name, within their table. To get create a lock, we first need to create a\n:class:`lynk.session.Session` that is bound to our table. The session can\nthen be used to create multiple locks that will be backed by that table.\n\nThe easiet way to make a :class:`lynk.session.Session` is by using the\n:func:`lynk.get_session` function. This function only takes one argument\nwhich is the name of the table it is bound to. Once a session has been created\nit can be used to create lock objects using the\n:meth:`lynk.session.Session.create_lock` method.\n\n.. code-block:: python\n\n import lynk\n\n session = lynk.get_session('lynk-quickstart')\n lock = session.create_lock('my lock')\n\n``lock`` is an instance of :class:`lynk.lock.Lock` which is bound to both our\ntable ``lynk-quickstart``, and the logical lock name ``my lock``. If we create\nanother lock object bound to the same table, with the same lock name, only one\nwill be acquireable at a time, with the second having to wait for the first one\nto release before being able to acquire it. This is a little bit awkard to\nshow in a single code segment since it requires muiltiple threads. Below is a\nminimal but complete example of using two threads to contend for the same lock.\n\n.. code-block:: python\n\n import time\n import logging\n import threading\n\n import lynk\n\n\n LOG = logging.getLogger(__file__)\n\n\n def configure_logging():\n LOG.setLevel(logging.DEBUG)\n formatter = logging.Formatter('%(threadName)s - %(message)s')\n ch = logging.StreamHandler()\n ch.setFormatter(formatter)\n LOG.addHandler(ch)\n\n\n def thread(session):\n LOG.debug('Starting')\n lock = session.create_lock('my lock')\n lock.acquire()\n LOG.debug('Lock acquired')\n time.sleep(10)\n lock.release()\n LOG.debug('Lock released')\n\n\n def main():\n configure_logging()\n session = lynk.get_session('lynk-quickstart')\n t1 = threading.Thread(target=thread, args=(session,))\n t2 = threading.Thread(target=thread, args=(session,))\n\n t1.start()\n t2.start()\n t1.join()\n t2.join()\n\n\n if __name__ == \"__main__\":\n main()\n\n\nFirst, we can ignore the ``configure_logging`` function, it just sets up\nlogging to show which thread is emitting the logs. This makes it easier to track\nthe flow of our program.\n\nLooking at the ``main`` function, the first real thing that happens we create\na session that can create locks bound to our table ``lynk-quickstart``.\n\n.. code-block:: python\n\n session = lynk.get_session('lynk-quickstart')\n\nWe then create two thread objects, and pass our ``session`` object into each\nas a shared variable. Once started each thread will execute the ``thread``\nfunction.\n\n.. code-block:: python\n\n t1 = threading.Thread(target=thread, args=(session,))\n t2 = threading.Thread(target=thread, args=(session,))\n\n\nThe last thing the ``main`` function does is start both threads, then join on\nthem, which will wait for them to complete before exiting.\n\n.. code-block:: python\n\n t1.start()\n t2.start()\n t1.join()\n t2.join()\n\n\nNow we have two threads executing the ``thread`` function. Following along each\nthread, disregarding the log statements, the first thing it does is create a\nlock object.\n\n.. code-block:: python\n\n lock = session.create_lock('my lock')\n\nThis means each thread will have its own unique lock object linked logically to\nthe name ``my lock``. The threads share a session, which is bound to the table\n``lynk-quickstart``. Simply creating the lock does not interact with the\nDynamoDB Tables in any way.\n\nNext each thread tries to acquire the lock.\n\n.. code-block:: python\n\n lock.acquire()\n\nThis simple statement is what makes the call to write an entry in our DynamoDB\nTable. Once an entry is written, this indicates that the lock is in-use and\nwe are safe to operate on whatever resource this lock was responsible for\nprotecting. In this example case we simply sleep for 10 seconds and then\nrelease the lock.\n\n.. code-block:: python\n\n time.sleep(10)\n lock.release()\n\nThe ``time.sleep(10)`` call would be replaced with real work in an actual\napplication. Once the protected resource is done being operated on, and has\nbeen safely written and is ready for another agent to use, we release the\nlock. The :meth:`lynk.lock.Lock.release` call deletes the entry from the table\nfreeing the lock name up to be used by another agent.\n\n\nThe output of our little sample application is shown below. You can see one\nthread gets the lock (in this case ``Thread-2``) and does it work while the\nother thread waits for it to be released. Once released, the other thread\nrepeats the same process::\n\n Thread-1 - Starting\n Thread-2 - Starting\n Thread-2 - Lock acquired\n Thread-2 - Lock released\n Thread-1 - Lock acquired\n Thread-1 - Lock released\n\n\nMore complex but similar examples can be seen in the\n`examples `_\ndirectory of the source repo.\n\n\nLock entry details\n------------------\n\nIf you have the AWS CLI installed you can run the following command while the\nexample script above is running (shouldn't be too difficult since the script\ntakes around 30 seconds to complete)::\n\n $ aws dynamodb scan --table-name lynk-quickstart --query Items\n [\n {\n\t \"lockKey\": {\n\t \"S\": \"my lock\"\n\t },\n\t \"leaseDuration\": {\n\t \"N\": \"20\"\n\t },\n\t \"versionNumber\": {\n\t \"S\": \"dabbbfde-93cb-47f8-a249-fbae84c4a5e3\"\n\t },\n\t \"hostIdentifier\": {\n\t \"S\": \"Johns-MacBook-Pro.local\"\n\t }\n }\n ]\n\nWhile the lock is held by a thread, we can see the entry that marks it as in\nuse. It has four components, the ``lockKey`` which is clearly the lock name\nthat we selected when creating our lock object. A ``leaseDuration``, this is\nthe amount of time we have a lease on this lock. Any other agent that wants\nto acquire this lock must wait at least that long before trying again. Our\nexample code will refresh this lock automatically, even if we had slept longer\nthan 20 seconds.\nThe ``versionNumber`` is used as a fencing token, each write to this entry\nchanges this value. You can read more about how the ``leaseDuration`` and\n``versionNumber`` are used to ensure the lock integrity in the documentation\nfor the :class:`lynk.techniques.VersionLeaseTechinque`. Finally there is a\n``hostIdentifier`` which is just there to show the host that created the lock.\nThis can be used for debugging a distributed multi-agent system all using one\nlock table.\n\nMore examples can be found in the\n`examples `_\ndirectory in the source repo.\n\n\nContext manager\n---------------\n\nIn the above example we manually call ``acquire()`` and ``release()``. This depends on no\nexceptions ocurring, and would generally be safer in a ``try: finally:`` block. For\nconvenience the :class:`lynk.lock.Lock` object can be called and used as a context manager.\nThe following code:\n\n.. code-block:: python\n\n lock.acquire()\n time.sleep(10)\n lock.release()\n\nCan be re-written more safely, and conveinently, as:\n\n.. code-block:: python\n\n with lock():\n time.sleep(10)\n\nThis ensures the releasing in the lock in the case of an unexpected exception.\n\n\nTeardown\n--------\n\nTo tear down the resources created during the quickstart tutorial run the\n``lynk delete-table`` command::\n\n $ lynk delete-table lynk-quickstart\n Deleting table lynk-quickstart\n Deleted\n\nVerify that there are no left over tables checking that the following has no\noutput::\n\n $ lynk list-tables\n\n\n.. quick-start-end\n\n\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/stealthycoin/lynk", "keywords": "", "license": "Apache License 2.0", "maintainer": "", "maintainer_email": "", "name": "lynk", "package_url": "https://pypi.org/project/lynk/", "platform": "", "project_url": "https://pypi.org/project/lynk/", "project_urls": { "Homepage": "https://github.com/stealthycoin/lynk" }, "release_url": "https://pypi.org/project/lynk/0.3.1/", "requires_dist": [ "boto3 (>=1.4.7)" ], "requires_python": "", "summary": "Client for using AWS DynamoDB as a distributed lock.", "version": "0.3.1" }, "last_serial": 4710287, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "5e73656891cbdb573ab4ffe5b7cff654", "sha256": "a83d1c837f2baa2634cd5b1c82294f9f8e22ced7b9adaa47713f8bf40ade5f5c" }, "downloads": -1, "filename": "lynk-0.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "5e73656891cbdb573ab4ffe5b7cff654", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 15674, "upload_time": "2018-12-12T07:40:08", "url": "https://files.pythonhosted.org/packages/16/55/fc3ffa565dc9275427383ffce138f25c0709fe17590773469f7dcced2922/lynk-0.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "acd9564d9edc7e459d8ada100aefef27", "sha256": "58004574b5a8e4e70b9a86d916987d90a73367d29729cc6723737736f3a3e589" }, "downloads": -1, "filename": "lynk-0.1.0.tar.gz", "has_sig": false, "md5_digest": "acd9564d9edc7e459d8ada100aefef27", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16576, "upload_time": "2018-12-12T07:40:10", "url": "https://files.pythonhosted.org/packages/46/a2/fcae0552995df8a43a10acfde689e5d023c6c025b2dad0568da1552d227e/lynk-0.1.0.tar.gz" } ], "0.3.0": [ { "comment_text": "", "digests": { "md5": "4bc074212dec74b91e1cccd53b50a03a", "sha256": "0eac4cac0e7a6ddfc65cce89ade8f34ffdecc7385e2578644d98148f0d55abce" }, "downloads": -1, "filename": "lynk-0.3.0-py3-none-any.whl", "has_sig": false, "md5_digest": "4bc074212dec74b91e1cccd53b50a03a", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 17644, "upload_time": "2019-01-17T18:58:59", "url": "https://files.pythonhosted.org/packages/69/c7/2c3c805cef64d23c901c04b153998a898fc03febe9ab6eac4179dc40472c/lynk-0.3.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c9385830ff17f0bdbc244ad311d40e9a", "sha256": "b06a4eb2ef082b71ce54b665fc7990b6b2deb4c72c357c54d9679749d235e096" }, "downloads": -1, "filename": "lynk-0.3.0.tar.gz", "has_sig": false, "md5_digest": "c9385830ff17f0bdbc244ad311d40e9a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18690, "upload_time": "2019-01-17T18:59:01", "url": "https://files.pythonhosted.org/packages/0a/91/abca0e9117eed9747ee37417987e8c1db6feb12a13e1c3b3a40e0b74944d/lynk-0.3.0.tar.gz" } ], "0.3.1": [ { "comment_text": "", "digests": { "md5": "91f4885d7a279587e378fdf701ba1b0c", "sha256": "af205f8f11b11ab6492735b2ffa9b9cb3f54dca88939b92849c4190f981eaac0" }, "downloads": -1, "filename": "lynk-0.3.1-py3-none-any.whl", "has_sig": false, "md5_digest": "91f4885d7a279587e378fdf701ba1b0c", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 17649, "upload_time": "2019-01-18T00:09:13", "url": "https://files.pythonhosted.org/packages/a6/2f/44030d7fe97fa82dddedd445da17dc7d52def952e04b22babffa6315cc6c/lynk-0.3.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "77e223a19bc9e8d9b85a8999b639d957", "sha256": "7781ae99809b2c375b7aad87986fff69f741e27c0b754d34ae3b978d92e4257b" }, "downloads": -1, "filename": "lynk-0.3.1.tar.gz", "has_sig": false, "md5_digest": "77e223a19bc9e8d9b85a8999b639d957", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18696, "upload_time": "2019-01-18T00:09:15", "url": "https://files.pythonhosted.org/packages/bc/88/dc7e82db08fcb1811ce686f5dcea93acd24c13c460f178a582ec4e9e175f/lynk-0.3.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "91f4885d7a279587e378fdf701ba1b0c", "sha256": "af205f8f11b11ab6492735b2ffa9b9cb3f54dca88939b92849c4190f981eaac0" }, "downloads": -1, "filename": "lynk-0.3.1-py3-none-any.whl", "has_sig": false, "md5_digest": "91f4885d7a279587e378fdf701ba1b0c", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 17649, "upload_time": "2019-01-18T00:09:13", "url": "https://files.pythonhosted.org/packages/a6/2f/44030d7fe97fa82dddedd445da17dc7d52def952e04b22babffa6315cc6c/lynk-0.3.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "77e223a19bc9e8d9b85a8999b639d957", "sha256": "7781ae99809b2c375b7aad87986fff69f741e27c0b754d34ae3b978d92e4257b" }, "downloads": -1, "filename": "lynk-0.3.1.tar.gz", "has_sig": false, "md5_digest": "77e223a19bc9e8d9b85a8999b639d957", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18696, "upload_time": "2019-01-18T00:09:15", "url": "https://files.pythonhosted.org/packages/bc/88/dc7e82db08fcb1811ce686f5dcea93acd24c13c460f178a582ec4e9e175f/lynk-0.3.1.tar.gz" } ] }