# Sync Socket¶

This is an extension of the MeshSocket which syncronizes a common dict. It works by providing an extra handler to store data. This exposes the entire dict API.

Note

This is a fairly inefficient architecture for write intensive applications. For cases where the majority of access is reading, or for small networks, this is ideal. For larger networks with infrequent access, you should use the chord socket.

## Basic Usage¶

There are three limitations compared to a normal dict.

1. Keys can only be bytes-like objects
2. Keys are automatically translated to bytes
3. By default, this implements a leasing system which prevents you from changing values set by others for a certain time

You can override the last restriction by constructing with leasing=False, like so:

>>> from py2p import sync
>>> sock = sync.SyncSocket('0.0.0.0', 4444, leasing=False)


The only other API differences between this and MeshSocket are for access to this dictionary. They are as follows.

### get() / __getitem__()¶

A value can be retrieved by using the get() method, or alternately with __getitem__(). These calls are both O(1), as they read from a local dict.

>>> 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 bytes before being used, so it is required that you use a bytes-like object. It is also safer to manually convert unicode keys to bytes, as there are sometimes inconsistencies betwen the Javascript and Python implementation. If you notice one of these, please file a bug report.

### set() / __setitem__()¶

A value can be stored by using the set() method, or alternately with __setitem__(). These calls are worst case O(n), as it has to change values on other nodes. More accurately, the delay between your node knowing of the change and the last node knowing of the change is between O(log(n)) and O(n).

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


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

This will raise a KeyError if another node has set this value already. Their lease will expire one hour after they set it. If two leases are started at the same UTC second, the tie is settled by doing a string compare of their IDs.

Any node which sets a value can change this value as well. Changing the value renews the lease on it.

### __delitem__()¶

Any node which owns a key, can clear its value. Doing this will relinquish your lease on that value. Like the above, this call is worst case O(n).

>>> del sock['test']


### update()¶

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

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


### keys() / values() / items()¶

These methods are analagous to the ones in Python’s 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.

### pop() / popitem()¶

These methods are also analagous to the ones in Python’s dict. The main difference is that if the leasing system is active, calling this method may throw an error if you don’t “own” whatever key is popped.

## Events¶

In addition to the above, and those of MeshSocket, the SyncSocket object has two Events.

First there’s Event 'update'(). This is called whenever an association is updated.

>>> @sock.on('update')
>>> def handle_key_update(conn, key, new_data, meta):
...     # conn is a reference to the socket
...     print("An association was updated: {} -> {}".format(key, new_data))
...     print("This change was made by {} at unix time {}".format(meta.owner, meta.timestamp))
...


This class has one other event: Event 'delete'(). This is called every time an association is removed.

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