gappleto97/p2p-project

View on GitHub
docs/python/tutorial/chord.rst

Summary

Maintainability
Test Coverage
Chord Socket
~~~~~~~~~~~~

This is an extension of the :doc:`MeshSocket <./mesh>` which syncronizes a common :py:class:`dict`. It works by providing some extra handlers to store data. This exposes the entire :py:class:`dict` API.

.. note::

    This is a fairly inefficient architecture for read intensive applications. For large databases which are infrequently changed, this is ideal. For smaller networks where there is significant access required, you should use the :doc:`sync <./sync>` socket.

Basic Usage
-----------

There are three limitations compared to a normal :py:class:`dict`.

1. Keys can only be :py:class:`bytes`-like objects
2. Keys are automatically translated to :py:class:`bytes`
3. Fetching values is significantly slower than for a :py:class:`dict`

The only API differences between this and :py:class:`~py2p.mesh.MeshSocket` are for access to this dictionary. They are as follows.

:py:meth:`~py2p.chord.ChordSocket.get` / :py:meth:`~py2p.chord.ChordSocket.__getitem__`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

A value can be retrieved by using the :py:meth:`~py2p.chord.ChordSocket.get` method, or alternately with :py:meth:`~py2p.chord.ChordSocket.__getitem__`. These calls are about ``O(log(n))`` hops, as they approximately halve their search area with each hop.

.. code-block:: python

    >>> foo = sock.get('test key', None)        # Returns None if there is nothing at that key
    >>> bar = sock[b'test key']                 # Raises KeyError if there is nothing at that key
    >>> assert bar == foo == sock[u'test key']  # Because of the translation mentioned below, these are the same key

It is important to note that keys are all translated to :py:class:`bytes` before being used, so it is required that you use a :py:class:`bytes`-like object. It is also safer to manually convert :py:class:`unicode` keys to :py:class:`bytes`, as there are sometimes inconsistencies betwen the Javascript and Python implementation. If you notice one of these, please file a bug report.

:py:meth:`~py2p.chord.ChordSocket.set` / :py:meth:`~py2p.chord.ChordSocket.__setitem__`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

A value can be stored by using the :py:meth:`~py2p.chord.ChordSocket.set` method, or alternately with :py:meth:`~py2p.chord.ChordSocket.__setitem__`. Like the above, these calls are about ``O(log(n))`` hops, as they approximately halve their search area with each hop.

.. code-block:: python

    >>> sock.set('test key', 'value')
    >>> sock[b'test key'] = b'value'
    >>> sock[u'测试'] = 'test'

Like above, keys and values are all translated to :py:class:`bytes` before being used, so it is required that you use a :py:class:`bytes`-like object.

:py:meth:`~py2p.chord.ChordSocket.__delitem__`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

This deletes an association. Like the above, this call is about ``O(log(n))``.

.. code-block:: python

    >>> del sock['test']

:py:meth:`~py2p.chord.ChordSocket.update`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The update method is simply a wrapper which updates based on a fed :py:class:`dict`. Essentially it runs the following:

.. code-block:: python

    >>> for key, value in update_dict.items():
    ...     sock[key] = value

:py:meth:`~py2p.chord.ChordSocket.keys` / :py:meth:`~py2p.chord.ChordSocket.values` / :py:meth:`~py2p.chord.ChordSocket.items`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

These methods are analagous to the ones in Python's :py:class:`dict`. The main difference is that they emulate the Python 3 behavior. So if you call these from Python 2, they will still return an iterator, rather than a list.

In addition, you should always surround :py:meth:`~py2p.chord.ChordSocket.values` and :py:meth:`~py2p.chord.ChordSocket.items` in a try-catch for :py:class:`KeyError` and :py:class:`socket.error`. Because the data is almost always stored on other nodes, you cannot guaruntee that an item in :py:meth:`~py2p.chord.ChordSocket.keys` is retrievable.

:py:meth:`~py2p.chord.ChordSocket.pop` / :py:meth:`~py2p.chord.ChordSocket.popitem`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

These methods are also analagous to the ones in Python's :py:class:`dict`. The main difference is that, like the above, you should always surround these in a try-catch for :py:class:`KeyError` and :py:class:`socket.error`.

Events
------

In addition to the above, and those of :py:class:`~py2p.mesh.MeshSocket`, the :py:class:`~py2p.chord.ChordSocket` object has two Events.

First there's |ChordSocket_onadd|_. This is called whenever an association is added. Because the value is not necessarily stored by you, it is not given as an argument to this event.

.. code-block:: python

    >>> @sock.on('add')
    >>> def handle_new_key(conn, key):
    ...     # conn is a reference to the socket
    ...     print("A new key was added: {}".format(key))
    ...

This class has one other event: |ChordSocket_ondelete|_. This is called every time an association is removed.

.. code-block:: python

    >>> sock.on('delete')
    >>> def handle_deleted_key(conn, key):
    ...     # conn is a reference to the socket
    ...     print("A key was deleted: {}".format(key))
    ...

Advanced Usage
--------------

Refer to :doc:`the mesh socket tutorial <./mesh>`

.. |MeshSocket_onconnect| replace:: :py:func:`~py2p.mesh.MeshSocket.Event 'connect'`
.. _MeshSocket_onconnect: ../mesh.html#MeshSocket.Event%20'connect'

.. |MeshSocket_onmessage| replace:: :py:func:`~py2p.mesh.MeshSocket.Event 'message'`
.. _MeshSocket_onmessage: ../mesh.html#MeshSocket.Event%20'message'

.. |ChordSocket_onadd| replace:: :py:func:`~py2p.chord.ChordSocket.Event 'add'`
.. _ChordSocket_onadd: ../chord.html#ChordSocket.Event%20'add'

.. |ChordSocket_ondelete| replace:: :py:func:`~py2p.chord.ChordSocket.Event 'delete'`
.. _ChordSocket_ondelete: ../chord.html#ChordSocket.Event%20'delete'