├── .gitignore ├── Procfile ├── README.md ├── crdt ├── __init__.py ├── constants.py ├── control │ ├── __init__.py │ ├── client_api.py │ ├── counter_api.py │ ├── register_api.py │ └── set_api.py ├── generate_key.py ├── key_utilities.py ├── model │ ├── TwoPSet.py │ ├── __init__.py │ ├── gcounter.py │ ├── gset.py │ ├── lwwregister.py │ ├── pncounter.py │ ├── pnset.py │ └── twoptwopgraph.py ├── redis_manager.py └── status_codes.py ├── docs └── README.md ├── pip-selfcheck.json ├── requirements.txt ├── run.py ├── runtime.txt └── tests └── sampletest.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | #Ipython Notebook 62 | .ipynb_checkpoints 63 | 64 | # Virtualenv 65 | .Python 66 | bin 67 | lib 68 | include 69 | 70 | # Mac OS X custom attribute files 71 | .DS_Store 72 | 73 | # Removing IDE files 74 | .idea 75 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn --log-file=- crdt:app -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Conflict-free Replicated Data Types (CRDTs) for Python 2 | 3 | A library, that provides Conflict-free Replicated Data Types (CRDTs) for distributed Python applications. The intent of this middleware is to provide a simple interface for handling CRDTs and to use them in Python applications. For more information on CRDT, read [here](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type). 4 | 5 | [Sai Teja Ranuva](https://github.com/saitejar) and I created this library for our CS 237 - Distributed Systems Middleware class, at University of California, Irvine in the Spring of 2016. 6 | 7 | ##Installation 8 | 9 | 1. Install Virtualenv, by following the instructions [here](https://virtualenv.pypa.io/en/latest/installation.html) 10 | 2. Clone this repository 11 | 3. Run the command `virtualenv crdt-py` when in the folder that has the cloned repository 12 | 4. Change directory to the project `cd crdt-py` 13 | 5. Run the activate script `source bin/activate` 14 | 6. Install the dependencies `pip install -r requirements.txt` 15 | 16 | NOTE - When done using and developing for this project, run the command `deactivate` 17 | 18 | ##Dependencies 19 | 20 | 1. This project needs a running copy of [Redis](http://redis.io/) locally. Get a copy of Redis, [here](http://redis.io/download). 21 | -------------------------------------------------------------------------------- /crdt/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | from crdt.constants import DATA_TYPES, NULL_TYPE, DUMMY_KEY 4 | from crdt.control.client_api import client_api_blueprint 5 | from crdt.control.counter_api import counter_api_blueprint 6 | from crdt.control.set_api import set_api_blueprint 7 | from crdt.control.register_api import register_api_blueprint 8 | from crdt.model.lwwregister import LWWERegister 9 | from redis_manager import redis_manager 10 | from flask.ext.cors import CORS 11 | 12 | app = Flask(__name__) 13 | CORS(app) 14 | 15 | app.register_blueprint(counter_api_blueprint, url_prefix='/counter') 16 | app.register_blueprint(set_api_blueprint, url_prefix='/set') 17 | app.register_blueprint(client_api_blueprint, url_prefix='/client') 18 | app.register_blueprint(register_api_blueprint, url_prefix='/register') 19 | 20 | 21 | @app.after_request 22 | def after_request(response): 23 | response.headers.add('Access-Control-Allow-Origin', '*') 24 | response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization') 25 | response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE') 26 | return response 27 | 28 | 29 | def run(): 30 | # print 'Flush Redis!' 31 | # redis_manager.flushdb() 32 | # redis_manager.flushall() 33 | app.run(debug=True) 34 | 35 | if __name__ == "__main__": 36 | run() 37 | -------------------------------------------------------------------------------- /crdt/constants.py: -------------------------------------------------------------------------------- 1 | G_COUNTER = 'gcounter' 2 | PN_COUNTER = 'pncounter' 3 | G_SET = 'gset' 4 | TWO_P_SET = '2pset' 5 | LWW_E_SET = 'lwweset' 6 | PN_SET = 'pnset' 7 | OR_SET = 'orset' 8 | LWW_REGISTER = 'lwwregister' 9 | MV_REGISTER = 'mvregister' 10 | TWO_P_TWO_P_GRAPH = 'twoptwopgraph' 11 | NULL_TYPE = 'nullltype' 12 | DATA_TYPES = 'datatypes' 13 | DUMMY_KEY = 'dummykey' 14 | CLIENTS = 'clients' 15 | ALL_CLIENTS = 'allclients' 16 | 17 | -------------------------------------------------------------------------------- /crdt/control/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kishore-narendran/crdt-py/b3f7666b0320f0489afcb2779b0f10bb70c391d9/crdt/control/__init__.py -------------------------------------------------------------------------------- /crdt/control/client_api.py: -------------------------------------------------------------------------------- 1 | import hot_redis 2 | from flask import Blueprint, request, jsonify 3 | from crdt.generate_key import generate_random_client_key 4 | from crdt.redis_manager import connection 5 | from crdt.constants import ALL_CLIENTS 6 | from crdt.status_codes import status_codes 7 | 8 | client_api_blueprint = Blueprint('Client', __name__) 9 | 10 | 11 | @client_api_blueprint.route('/new', methods=['GET']) 12 | def create_new_client(): 13 | client_id = request.args.get('client_id') 14 | result_dict = dict() 15 | # Getting all client IDs already assigned 16 | all_clients = hot_redis.Set(key=ALL_CLIENTS, client=connection) 17 | 18 | # Checking if the client id in request is None or an empty string 19 | if client_id is None or len(client_id) is 0: 20 | client_id = generate_random_client_key() 21 | while client_id in all_clients: 22 | client_id = generate_random_client_key() 23 | # If client ID in request is valid checking if the client ID is already present 24 | else: 25 | if client_id in all_clients: 26 | result_dict['status'] = status_codes['existing_client_id'] 27 | return jsonify(result_dict) 28 | 29 | # Adding the client ID to the all clients set 30 | all_clients.add(client_id) 31 | result_dict['status'] = status_codes['success'] 32 | result_dict['client_id'] = client_id 33 | return jsonify(result_dict) 34 | 35 | 36 | -------------------------------------------------------------------------------- /crdt/control/counter_api.py: -------------------------------------------------------------------------------- 1 | import hot_redis 2 | from flask import Blueprint, request, jsonify 3 | 4 | from crdt.constants import DATA_TYPES, G_COUNTER, ALL_CLIENTS, PN_COUNTER 5 | from crdt.generate_key import generate_random_crdt_key 6 | from crdt.redis_manager import connection 7 | from crdt.model.gcounter import GCounter 8 | from crdt.model.pncounter import PNCounter 9 | from crdt.status_codes import status_codes 10 | from crdt.key_utilities import get_client_list_key, get_client_key 11 | 12 | 13 | counter_api_blueprint = Blueprint('Counter', __name__) 14 | 15 | 16 | @counter_api_blueprint.route("/g/new", methods=['GET']) 17 | def new_g_counter(): 18 | key = request.args.get('key') 19 | client_id = request.args.get('client_id') 20 | 21 | result_dict = dict() 22 | 23 | # Getting all clients and data types of all CRDTs 24 | all_clients = hot_redis.Set(key=ALL_CLIENTS, client=connection) 25 | data_types = hot_redis.Dict(key=DATA_TYPES, client=connection) 26 | 27 | # Checking if the client ID is valid 28 | if client_id not in all_clients: 29 | print 'Missing Client ID' 30 | result_dict['status'] = status_codes['client_id_not_found'] 31 | return jsonify(result_dict) 32 | 33 | # Checking if an empty or null key has been given, and generating key 34 | if key is None or len(key) is 0: 35 | key = generate_random_crdt_key() 36 | while key in data_types.keys(): 37 | key = generate_random_crdt_key() 38 | print 'Generated new random CRDT key' 39 | # Checking if the key has already been taken 40 | elif key in data_types.keys() and data_types[key] != G_COUNTER: 41 | result_dict['status'] = status_codes['data_type_mismatch'] 42 | return jsonify(result_dict) 43 | 44 | # All conditions met for key and client ID 45 | new_g_counter = GCounter(key=key) 46 | new_g_counter.add_client(client_id) 47 | result_dict['status'] = status_codes['success'] 48 | result_dict['key'] = key 49 | result_dict['data_type'] = data_types[key] 50 | result_dict['client_id'] = client_id 51 | return jsonify(result_dict) 52 | 53 | 54 | @counter_api_blueprint.route("/g/set", methods=['GET']) 55 | def set_g_counter(): 56 | key = request.args.get('key') 57 | client_id = request.args.get('client_id') 58 | value = request.args.get('value') 59 | 60 | result_dict = dict() 61 | 62 | check = check_input_fault(key, client_id, value, "default", G_COUNTER) 63 | if check is False: 64 | gcounter = GCounter(key=key) 65 | gcounter.set(client_id, int(value)) 66 | result_dict['status'] = status_codes['success'] 67 | result_dict['value'] = value 68 | return jsonify(result_dict) 69 | else: 70 | result_dict = check 71 | return jsonify(result_dict) 72 | 73 | 74 | @counter_api_blueprint.route("/g/get", methods=['GET']) 75 | def get_g_counter(): 76 | key = request.args.get('key') 77 | client_id = request.args.get('client_id') 78 | 79 | result_dict = dict() 80 | 81 | check = check_input_fault(key, client_id, "default", "default", G_COUNTER) 82 | if check is False: 83 | gcounter = GCounter(key=key) 84 | counter = gcounter.get(client_id) 85 | result_dict['counter'] = counter 86 | result_dict['status'] = status_codes['success'] 87 | return jsonify(result_dict) 88 | else: 89 | result_dict = check 90 | return jsonify(result_dict) 91 | 92 | 93 | @counter_api_blueprint.route("/pn/new", methods=['GET']) 94 | def new_pn_counter(): 95 | key = request.args.get('key') 96 | client_id = request.args.get('client_id') 97 | 98 | result_dict = dict() 99 | 100 | # Getting all clients and data types of all CRDTs 101 | all_clients = hot_redis.Set(key=ALL_CLIENTS, client=connection) 102 | data_types = hot_redis.Dict(key=DATA_TYPES, client=connection) 103 | 104 | # Checking if the client ID is valid 105 | if client_id not in all_clients: 106 | print 'Missing Client ID' 107 | result_dict['status'] = status_codes['client_id_not_found'] 108 | return jsonify(result_dict) 109 | 110 | # Checking if an empty or null key has been given, and generating key 111 | if key is None or len(key) is 0: 112 | key = generate_random_crdt_key() 113 | while key in data_types.keys(): 114 | key = generate_random_crdt_key() 115 | print 'Generated new random CRDT key' 116 | 117 | # Checking if the key has already been taken 118 | elif key in data_types.keys() and data_types[key] != PN_COUNTER: 119 | result_dict['status'] = status_codes['data_type_mismatch'] 120 | return jsonify(result_dict) 121 | 122 | # All conditions met for key and client ID 123 | pn_counter = PNCounter(key=key) 124 | pn_counter.add_client(client_id) 125 | result_dict['status'] = status_codes['success'] 126 | result_dict['key'] = key 127 | result_dict['data_type'] = data_types[key] 128 | result_dict['client_id'] = client_id 129 | return jsonify(result_dict) 130 | 131 | 132 | @counter_api_blueprint.route("/pn/set", methods=['GET']) 133 | def set_pn_counter(): 134 | key = request.args.get('key') 135 | client_id = request.args.get('client_id') 136 | pvalue = request.args.get('pvalue') 137 | nvalue = request.args.get('nvalue') 138 | 139 | result_dict = dict() 140 | 141 | check = check_input_fault(key, client_id, pvalue, nvalue, PN_COUNTER) 142 | if check is False: 143 | pncounter = PNCounter(key=key) 144 | pncounter.set(client_id, int(pvalue), int(nvalue)) 145 | result_dict['status'] = status_codes['success'] 146 | result_dict['pvalue'] = pvalue 147 | result_dict['nvalue'] = nvalue 148 | return jsonify(result_dict) 149 | else: 150 | result_dict = check 151 | return jsonify(result_dict) 152 | 153 | 154 | @counter_api_blueprint.route("/pn/get", methods=['GET']) 155 | def get_pn_counter(): 156 | key = request.args.get('key') 157 | client_id = request.args.get('client_id') 158 | 159 | result_dict = dict() 160 | 161 | check = check_input_fault(key, client_id, "default", "default", PN_COUNTER) 162 | if check is False: 163 | pncounter = PNCounter(key=key) 164 | counter = pncounter.get(client_id) 165 | result_dict['counter'] = counter 166 | result_dict['status'] = status_codes['success'] 167 | return jsonify(result_dict) 168 | else: 169 | result_dict = check 170 | return jsonify(result_dict) 171 | 172 | 173 | def check_input_fault(key, client_id, value1, value2, data_type): 174 | result_dict = dict() 175 | 176 | # Checking for valid key 177 | if key is None or len(key) == 0: 178 | result_dict['status'] = status_codes['missing_key_or_state'] 179 | return result_dict 180 | 181 | # Checking for valid client ID 182 | if client_id is None or len(client_id) == 0: 183 | result_dict['status'] = status_codes['client_id_not_found'] 184 | return result_dict 185 | 186 | # Checking for valid value1 187 | if (value1 is None or len(value1) == 0) and value1 != "default": 188 | result_dict['status'] = status_codes['value_not_found'] 189 | return result_dict 190 | 191 | # Checking for valid value2 192 | if (value2 is None or len(value2) == 0) and value2 != "default": 193 | result_dict['status'] = status_codes['value_not_found'] 194 | return result_dict 195 | 196 | data_types = hot_redis.Dict(key=DATA_TYPES, client=connection) 197 | all_clients = hot_redis.Set(key=ALL_CLIENTS, client=connection) 198 | client_list = hot_redis.Set(key=get_client_list_key(key), client=connection) 199 | 200 | # Checking if client ID is valid 201 | if client_id not in all_clients: 202 | result_dict['status'] = status_codes['client_id_not_found'] 203 | return result_dict 204 | 205 | # Checking if key is present 206 | if key not in data_types.keys(): 207 | result_dict['status'] = status_codes['missing_key_or_state'] 208 | return result_dict 209 | 210 | # Checking if the key is the right type 211 | if data_types[key] != data_type: 212 | result_dict['status'] = status_codes['data_type_mismatch'] 213 | return result_dict 214 | 215 | # Checking if client is in the GCounter's client list 216 | if get_client_key(key=key, client_id=client_id) not in client_list: 217 | result_dict['status'] = status_codes['client_id_not_in_crdt'] 218 | return result_dict 219 | 220 | return False 221 | -------------------------------------------------------------------------------- /crdt/control/register_api.py: -------------------------------------------------------------------------------- 1 | import hot_redis 2 | from flask import Blueprint, request, jsonify 3 | 4 | from crdt.constants import DATA_TYPES, LWW_REGISTER, ALL_CLIENTS 5 | from crdt.generate_key import generate_random_crdt_key 6 | from crdt.redis_manager import connection 7 | from crdt.model.lwwregister import LWWERegister 8 | from crdt.status_codes import status_codes 9 | from crdt.key_utilities import get_client_list_key, get_client_key 10 | 11 | 12 | register_api_blueprint = Blueprint('Register', __name__) 13 | 14 | 15 | @register_api_blueprint.route('/lwwe/new', methods=['GET']) 16 | def new_lwwe_register(): 17 | key = request.args.get('key') 18 | client_id = request.args.get('client_id') 19 | 20 | result_dict = dict() 21 | 22 | # Getting all clients and data types of all CRDTs 23 | all_clients = hot_redis.Set(key=ALL_CLIENTS, client=connection) 24 | data_types = hot_redis.Dict(key=DATA_TYPES, client=connection) 25 | 26 | # Checking if the client ID is valid 27 | if client_id not in all_clients: 28 | print 'Missing Client ID' 29 | result_dict['status'] = status_codes['client_id_not_found'] 30 | return jsonify(result_dict) 31 | 32 | # Checking if an empty or null key has been given, and generating key 33 | if key is None or len(key) is 0: 34 | key = generate_random_crdt_key() 35 | while key in data_types.keys(): 36 | key = generate_random_crdt_key() 37 | print 'Generated new random CRDT key' 38 | 39 | # Checking if the key has already been taken 40 | elif key in data_types.keys() and data_types[key] != LWW_REGISTER: 41 | result_dict['status'] = status_codes['data_type_mismatch'] 42 | return jsonify(result_dict) 43 | 44 | new_lwwe_register = LWWERegister(key=key) 45 | new_lwwe_register.add_client(client_id) 46 | result_dict['status'] = status_codes['success'] 47 | result_dict['key'] = key 48 | result_dict['data_type'] = data_types[key] 49 | result_dict['client_id'] = client_id 50 | return jsonify(result_dict) 51 | 52 | 53 | @register_api_blueprint.route("/lwwe/set", methods=['GET']) 54 | def set_lwwe_register(): 55 | key = request.args.get('key') 56 | client_id = request.args.get('client_id') 57 | value = request.args.get('value') 58 | timestamp = request.args.get('timestamp') 59 | 60 | result_dict = dict() 61 | 62 | check = check_input_fault(key, client_id, timestamp, LWW_REGISTER) 63 | if check is False: 64 | timestamp = float(timestamp) 65 | lwweregister = LWWERegister(key=key) 66 | lwweregister.set(client_id, value, timestamp) 67 | result_dict['status'] = status_codes['success'] 68 | result_dict['value'] = value 69 | result_dict['timestamp'] = timestamp 70 | return jsonify(result_dict) 71 | else: 72 | result_dict = check 73 | return jsonify(result_dict) 74 | 75 | 76 | @register_api_blueprint.route("/lwwe/get", methods=['GET']) 77 | def get_lwwe_register(): 78 | key = request.args.get('key') 79 | client_id = request.args.get('client_id') 80 | 81 | result_dict = dict() 82 | 83 | check = check_input_fault(key, client_id, -1.0, LWW_REGISTER) 84 | if check is False: 85 | lwweregister = LWWERegister(key=key) 86 | register_value = lwweregister.get(client_id) 87 | result_dict['value'] = eval(register_value)['value'] 88 | result_dict['timestamp'] = eval(register_value)['timestamp'] 89 | result_dict['status'] = status_codes['success'] 90 | return jsonify(result_dict) 91 | else: 92 | result_dict = check 93 | return jsonify(result_dict) 94 | 95 | 96 | def check_input_fault(key, client_id, timestamp, data_type): 97 | result_dict = dict() 98 | 99 | # Checking for valid key 100 | if key is None or len(key) == 0: 101 | result_dict['status'] = status_codes['missing_key_or_state'] 102 | return result_dict 103 | 104 | # Checking for valid client ID 105 | if client_id is None or len(client_id) == 0: 106 | result_dict['status'] = status_codes['client_id_not_found'] 107 | return result_dict 108 | 109 | # Checking validity of timestamp 110 | if timestamp != -1.0: 111 | try: 112 | timestamp = float(timestamp) 113 | except ValueError: 114 | result_dict['status'] = status_codes['timestamp_not_valid'] 115 | return result_dict 116 | 117 | data_types = hot_redis.Dict(key=DATA_TYPES, client=connection) 118 | all_clients = hot_redis.Set(key=ALL_CLIENTS, client=connection) 119 | client_list = hot_redis.Set(key=get_client_list_key(key), client=connection) 120 | 121 | # Checking if client ID is valid 122 | if client_id not in all_clients: 123 | result_dict['status'] = status_codes['client_id_not_found'] 124 | return result_dict 125 | 126 | # Checking if key is present 127 | if key not in data_types.keys(): 128 | result_dict['status'] = status_codes['missing_key_or_state'] 129 | return result_dict 130 | 131 | # Checking if the key is the right type 132 | if data_types[key] != data_type: 133 | result_dict['status'] = status_codes['data_type_mismatch'] 134 | return result_dict 135 | 136 | # Checking if client is in the GCounter's client list 137 | if get_client_key(key=key, client_id=client_id) not in client_list: 138 | result_dict['status'] = status_codes['client_id_not_in_crdt'] 139 | return result_dict 140 | 141 | return False -------------------------------------------------------------------------------- /crdt/control/set_api.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint, jsonify, request 2 | from crdt.generate_key import generate_random_crdt_key 3 | from crdt.constants import ALL_CLIENTS, DATA_TYPES, G_SET, TWO_P_SET 4 | from crdt.key_utilities import get_client_list_key, get_client_key 5 | from crdt.model.gset import GSet 6 | from crdt.model.twopset import TwoPSet 7 | from crdt.status_codes import status_codes 8 | from crdt.redis_manager import connection 9 | import hot_redis 10 | 11 | 12 | set_api_blueprint = Blueprint('Set', __name__) 13 | 14 | 15 | @set_api_blueprint.route('/g/new', methods=['GET']) 16 | def new_g_set(): 17 | key = request.args.get('key') 18 | client_id = request.args.get('client_id') 19 | 20 | result_dict = dict() 21 | 22 | # Getting all clients and data types of all CRDTs 23 | all_clients = hot_redis.Set(key=ALL_CLIENTS, client=connection) 24 | data_types = hot_redis.Dict(key=DATA_TYPES, client=connection) 25 | 26 | # Checking if the client ID is valid 27 | if client_id not in all_clients: 28 | print 'Missing Client ID' 29 | result_dict['status'] = status_codes['client_id_not_found'] 30 | return jsonify(result_dict) 31 | 32 | # Checking if an empty or null key has been given, and generating key 33 | if key is None or len(key) is 0: 34 | key = generate_random_crdt_key() 35 | while key in data_types.keys(): 36 | key = generate_random_crdt_key() 37 | print 'Generated new random CRDT key' 38 | 39 | # Checking if the key has already been taken 40 | elif key in data_types.keys() and data_types[key] != G_SET: 41 | result_dict['status'] = status_codes['data_type_mismatch'] 42 | return jsonify(result_dict) 43 | 44 | new_g_set = GSet(key=key) 45 | new_g_set.add_client(client_id) 46 | result_dict['status'] = status_codes['success'] 47 | result_dict['key'] = key 48 | result_dict['data_type'] = data_types[key] 49 | result_dict['client_id'] = client_id 50 | return jsonify(result_dict) 51 | 52 | 53 | @set_api_blueprint.route("/g/set", methods=['GET']) 54 | def set_g_set(): 55 | key = request.args.get('key') 56 | client_id = request.args.get('client_id') 57 | items = request.args.get('items') 58 | 59 | result_dict = dict() 60 | 61 | check = check_input_fault(key, client_id, G_SET) 62 | if check is False: 63 | gset = GSet(key=key) 64 | gset.set(client_id, items) 65 | result_dict['status'] = status_codes['success'] 66 | result_dict['items'] = list(eval(items)) 67 | return jsonify(result_dict) 68 | else: 69 | result_dict = check 70 | return jsonify(result_dict) 71 | 72 | 73 | @set_api_blueprint.route("/g/get", methods=['GET']) 74 | def get_g_set(): 75 | key = request.args.get('key') 76 | client_id = request.args.get('client_id') 77 | 78 | result_dict = dict() 79 | 80 | check = check_input_fault(key, client_id, G_SET) 81 | if check is False: 82 | gset = GSet(key=key) 83 | counter = gset.get(client_id) 84 | result_dict['set'] = list(eval(counter)) 85 | result_dict['status'] = status_codes['success'] 86 | return jsonify(result_dict) 87 | else: 88 | result_dict = check 89 | return jsonify(result_dict) 90 | 91 | 92 | @set_api_blueprint.route('/twop/new', methods=['GET']) 93 | def new_twop_set(): 94 | key = request.args.get('key') 95 | client_id = request.args.get('client_id') 96 | 97 | result_dict = dict() 98 | 99 | # Getting all clients and data types of all CRDTs 100 | all_clients = hot_redis.Set(key=ALL_CLIENTS, client=connection) 101 | data_types = hot_redis.Dict(key=DATA_TYPES, client=connection) 102 | 103 | # Checking if the client ID is valid 104 | if client_id not in all_clients: 105 | print 'Missing Client ID' 106 | result_dict['status'] = status_codes['client_id_not_found'] 107 | return jsonify(result_dict) 108 | 109 | # Checking if an empty or null key has been given, and generating key 110 | if key is None or len(key) is 0: 111 | key = generate_random_crdt_key() 112 | while key in data_types.keys(): 113 | key = generate_random_crdt_key() 114 | print 'Generated new random CRDT key' 115 | 116 | # Checking if the key has already been taken 117 | elif key in data_types.keys() and data_types[key] != TWO_P_SET: 118 | result_dict['status'] = status_codes['data_type_mismatch'] 119 | return jsonify(result_dict) 120 | 121 | new_twop_set = TwoPSet(key=key) 122 | new_twop_set.add_client(client_id) 123 | result_dict['status'] = status_codes['success'] 124 | result_dict['key'] = key 125 | result_dict['data_type'] = data_types[key] 126 | result_dict['client_id'] = client_id 127 | return jsonify(result_dict) 128 | 129 | 130 | @set_api_blueprint.route("/twop/set", methods=['GET']) 131 | def set_twop_set(): 132 | key = request.args.get('key') 133 | client_id = request.args.get('client_id') 134 | add_items = request.args.get('add_items') 135 | delete_items = request.args.get('delete_items') 136 | 137 | result_dict = dict() 138 | 139 | check = check_input_fault(key, client_id, TWO_P_SET) 140 | if check is False: 141 | twop_set = TwoPSet(key=key) 142 | twop_set.set(client_id, add_items, delete_items) 143 | result_dict['status'] = status_codes['success'] 144 | result_dict['add_items'] = list(eval(add_items)) 145 | result_dict['delete_items'] = list(eval(delete_items)) 146 | return jsonify(result_dict) 147 | else: 148 | result_dict = check 149 | return jsonify(result_dict) 150 | 151 | 152 | @set_api_blueprint.route("/twop/check", methods=['GET']) 153 | def check_twop_set(): 154 | key = request.args.get('key') 155 | client_id = request.args.get('client_id') 156 | item = request.args.get('item') 157 | 158 | result_dict = dict() 159 | 160 | check = check_input_fault(key, client_id, TWO_P_SET) 161 | if check is False: 162 | twop_set = TwoPSet(key=key) 163 | is_found = twop_set.check(client_id, item) 164 | result_dict['status'] = status_codes['success'] 165 | result_dict['found'] = is_found 166 | return jsonify(result_dict) 167 | else: 168 | result_dict = check 169 | return jsonify(result_dict) 170 | 171 | 172 | @set_api_blueprint.route("/twop/get", methods=['GET']) 173 | def get_twop_set(): 174 | key = request.args.get('key') 175 | client_id = request.args.get('client_id') 176 | 177 | result_dict = dict() 178 | 179 | check = check_input_fault(key, client_id, TWO_P_SET) 180 | if check is False: 181 | twop_set = TwoPSet(key=key) 182 | result_set = twop_set.get(client_id) 183 | result_dict['set'] = list(eval(result_set)) 184 | result_dict['status'] = status_codes['success'] 185 | return jsonify(result_dict) 186 | else: 187 | result_dict = check 188 | return jsonify(result_dict) 189 | 190 | 191 | def check_input_fault(key, client_id, data_type): 192 | result_dict = dict() 193 | 194 | # Checking for valid key 195 | if key is None or len(key) == 0: 196 | result_dict['status'] = status_codes['missing_key_or_state'] 197 | return result_dict 198 | 199 | # Checking for valid client ID 200 | if client_id is None or len(client_id) == 0: 201 | result_dict['status'] = status_codes['client_id_not_found'] 202 | return result_dict 203 | 204 | data_types = hot_redis.Dict(key=DATA_TYPES, client=connection) 205 | all_clients = hot_redis.Set(key=ALL_CLIENTS, client=connection) 206 | client_list = hot_redis.Set(key=get_client_list_key(key), client=connection) 207 | 208 | # Checking if client ID is valid 209 | if client_id not in all_clients: 210 | result_dict['status'] = status_codes['client_id_not_found'] 211 | return result_dict 212 | 213 | # Checking if key is present 214 | if key not in data_types.keys(): 215 | result_dict['status'] = status_codes['missing_key_or_state'] 216 | return result_dict 217 | 218 | # Checking if the key is the right type 219 | if data_types[key] != data_type: 220 | result_dict['status'] = status_codes['data_type_mismatch'] 221 | return result_dict 222 | 223 | # Checking if client is in the GCounter's client list 224 | if get_client_key(key=key, client_id=client_id) not in client_list: 225 | result_dict['status'] = status_codes['client_id_not_in_crdt'] 226 | return result_dict 227 | 228 | return False 229 | -------------------------------------------------------------------------------- /crdt/generate_key.py: -------------------------------------------------------------------------------- 1 | 2 | import random 3 | 4 | 5 | def generate_random_crdt_key(): 6 | key = ''.join(random.choice('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(16)) 7 | return key 8 | 9 | 10 | def generate_random_client_key(): 11 | key = ''.join(random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(4)) 12 | return key -------------------------------------------------------------------------------- /crdt/key_utilities.py: -------------------------------------------------------------------------------- 1 | from constants import CLIENTS 2 | 3 | 4 | def get_client_list_key(key): 5 | return key + '_' + CLIENTS 6 | 7 | 8 | def get_client_key(key, client_id): 9 | return key + '_' + client_id 10 | 11 | 12 | def get_pcounter_key(key): 13 | return key + 'P' 14 | 15 | 16 | def get_ncounter_key(key): 17 | return key + 'N' 18 | 19 | 20 | def get_add_set_key(key): 21 | return key + 'A' 22 | 23 | 24 | def get_delete_set_key(key): 25 | return key + 'D' 26 | 27 | 28 | def get_nodes_set_key(key): 29 | return key + '_NODES' 30 | 31 | 32 | def get_edges_set_key(key): 33 | return key + '_EDGES' 34 | 35 | 36 | def get_pnset_key(key): 37 | return key + 'PN_SET' 38 | 39 | 40 | def get_pncounter_item_pnset_key(key, item): 41 | return key + '_' + item 42 | -------------------------------------------------------------------------------- /crdt/model/TwoPSet.py: -------------------------------------------------------------------------------- 1 | import hot_redis 2 | 3 | from crdt.constants import DATA_TYPES, TWO_P_SET 4 | from crdt.key_utilities import get_client_list_key, get_client_key, get_add_set_key, get_delete_set_key 5 | from crdt.redis_manager import connection 6 | from crdt.model.gset import GSet 7 | from random import random 8 | 9 | 10 | class TwoPSet: 11 | def __init__(self, key): 12 | # Setting the key of the PN Counter Instance 13 | self.key = key 14 | self.add_set = GSet(get_add_set_key(key)) 15 | self.delete_set = GSet(get_delete_set_key(key)) 16 | 17 | # Getting/Setting the client list and type of the GCounter instance 18 | self.client_list = hot_redis.Set(key=get_client_list_key(key), client=connection) 19 | hot_redis.Dict(key=DATA_TYPES, client=connection)[key] = TWO_P_SET 20 | 21 | def add_client(self, client_id): 22 | # Adding client ID to client list 23 | new_client = get_client_key(self.key, client_id) 24 | self.client_list.add(new_client) 25 | 26 | # Adding clients to component GCounters 27 | self.add_set.add_client(client_id) 28 | self.delete_set.add_client(client_id) 29 | 30 | def get(self, client_id): 31 | add_set_value = self.add_set.get(client_id) 32 | delete_set_value = self.delete_set.get(client_id) 33 | final_set = set(eval(add_set_value)).difference(set(eval(delete_set_value))) 34 | return repr(final_set) 35 | 36 | def check(self, val, client_id=None): 37 | if client_id is None: 38 | final_set = self.get(random.choice(list(self.client_list))) 39 | else: 40 | final_set = self.get(client_id) 41 | final_set = eval(final_set) 42 | if val in final_set: 43 | return True 44 | else: 45 | return False 46 | 47 | def set(self, client_id, add_set, delete_set): 48 | self.add_set.set(client_id, add_set) 49 | self.delete_set.set(client_id, delete_set) 50 | 51 | -------------------------------------------------------------------------------- /crdt/model/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /crdt/model/gcounter.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | import hot_redis 4 | 5 | from crdt.constants import DATA_TYPES, G_COUNTER 6 | from crdt.key_utilities import get_client_list_key, get_client_key 7 | from crdt.redis_manager import connection 8 | 9 | 10 | class GCounter: 11 | def __init__(self, key): 12 | # Setting the key of the GCounter Instance 13 | self.key = key 14 | 15 | # Getting/Setting the client list and type of the GCounter instance 16 | self.client_list = hot_redis.Set(key=get_client_list_key(key), client=connection) 17 | hot_redis.Dict(key=DATA_TYPES, client=connection)[key] = G_COUNTER 18 | 19 | def add_client(self, client_id): 20 | # Generating a client list key for this key 21 | new_client = get_client_key(self.key, client_id) 22 | with hot_redis.transaction(): 23 | # Adding a new state to all the existing clients 24 | for client in self.client_list: 25 | hot_redis.Dict(key=client, client=connection)[new_client] = 0 26 | 27 | # Adding client to GCounter instance's client list 28 | self.client_list.add(new_client) 29 | 30 | # Adding a new state dictionary for this GCounter client 31 | new_client_state = hot_redis.Dict(key=new_client, client=connection) 32 | for client in self.client_list: 33 | new_client_state[client] = 0 34 | 35 | def get(self, client_id=None): 36 | # Getting the client's key for this GCounter 37 | if client_id is None: 38 | current_client_key = random.choice(list(self.client_list.value)) 39 | else: 40 | current_client_key = get_client_key(self.key, client_id) 41 | 42 | # Merging state from every other client for this GCounter 43 | for client in self.client_list: 44 | self.merge(current_client_key, client) 45 | 46 | # Updating the states from all other clients as the client's latest state 47 | current_client_state = hot_redis.Dict(key=current_client_key, client=connection) 48 | 49 | # Getting the final merged value 50 | count = 0 51 | for val in current_client_state.values(): 52 | count += int(val) 53 | 54 | return count 55 | 56 | def merge(self, client_a, client_b): 57 | # Getting Client A and Client B's state 58 | client_a_state = hot_redis.Dict(key=client_a, client=connection) 59 | client_b_state = hot_redis.Dict(key=client_b, client=connection) 60 | 61 | # Merging Client A's state with Client B's state and storing in Client A's State 62 | print self.client_list 63 | for client in self.client_list: 64 | print client 65 | print client_a_state 66 | client_a_state[client] = max(int(client_a_state[client]), int(client_b_state[client])) 67 | 68 | def peek(self, client_id): 69 | # Generating the current client's key for GCounter 70 | current_client_key = get_client_key(self.key, client_id) 71 | 72 | # Peek at the current value 73 | return int(hot_redis.Dict(key=current_client_key, client=connection)[current_client_key]) 74 | 75 | def increment(self, client_id, inc=1): 76 | self.set(client_id, self.peek(client_id) + int(inc)) 77 | 78 | def set(self, client_id, val): 79 | # Generating the current client's key for GCounter 80 | current_client_key = get_client_key(self.key, client_id) 81 | 82 | # Updating the client's state with the value to be set 83 | hot_redis.Dict(key=current_client_key, client=connection)[current_client_key] = val 84 | 85 | def exists(self, client_id): 86 | if client_id in self.client_list: 87 | return True 88 | else: 89 | return False 90 | -------------------------------------------------------------------------------- /crdt/model/gset.py: -------------------------------------------------------------------------------- 1 | import hot_redis 2 | 3 | from crdt.constants import DATA_TYPES, G_SET 4 | from crdt.key_utilities import get_client_list_key, get_client_key 5 | from crdt.redis_manager import connection 6 | 7 | 8 | class GSet: 9 | def __init__(self, key): 10 | # Setting the key of the GSet Instance 11 | self.key = key 12 | 13 | # Getting/Setting the client list and type of the GSet instance 14 | self.client_list = hot_redis.Set(key=get_client_list_key(key), client=connection) 15 | hot_redis.Dict(key=DATA_TYPES, client=connection)[key] = G_SET 16 | 17 | def add_client(self, client_id): 18 | # Generating a client list key for this key 19 | new_client = get_client_key(self.key, client_id) 20 | 21 | with hot_redis.transaction(): 22 | # Adding a new state to all the existing clients 23 | for client in self.client_list: 24 | hot_redis.Dict(key=client, client=connection)[new_client] = repr(set()) 25 | 26 | # Adding client to GSet instance's client list 27 | self.client_list.add(new_client) 28 | 29 | # Adding a new state dictionary for this GSet client 30 | new_client_state = hot_redis.Dict(key=new_client, client=connection) 31 | for client in self.client_list: 32 | new_client_state[client] = repr(set()) 33 | 34 | def get(self, client_id): 35 | # Getting the client's key for this GSet 36 | current_client_key = get_client_key(self.key, client_id) 37 | 38 | # Merging state from every other client for this GSet 39 | for client in self.client_list: 40 | self.merge(current_client_key, client) 41 | 42 | # Updating the states from all other clients as the client's latest state 43 | current_client_state = hot_redis.Dict(key=current_client_key, client=connection) 44 | 45 | # Getting the final merged value 46 | count = set() 47 | for val in current_client_state.values(): 48 | count = count.union(eval(val)) 49 | 50 | current_client_state[current_client_key] = repr(count) 51 | print repr(count) 52 | print current_client_state[current_client_key] 53 | 54 | return str(list(count)) 55 | 56 | def merge(self, client_a, client_b): 57 | # Getting Client A and Client B's state 58 | client_a_state = hot_redis.Dict(key=client_a, client=connection) 59 | client_b_state = hot_redis.Dict(key=client_b, client=connection) 60 | 61 | # Merging Client A's state with Client B's state and storing in Client A's State 62 | for client in self.client_list: 63 | client_a_state[client] = repr(eval(client_a_state[client]).union(eval(client_b_state[client]))) 64 | 65 | def set(self, client_id, val): 66 | 67 | # Generating the current client's key for GSet 68 | current_client_key = get_client_key(self.key, client_id) 69 | 70 | # Updating the client's state with the value to be set 71 | hot_redis.Dict(key=current_client_key, client=connection)[current_client_key] = repr(set(eval(val))) 72 | 73 | def exists(self, client_id): 74 | if client_id in self.client_list: 75 | return True 76 | else: 77 | return False 78 | -------------------------------------------------------------------------------- /crdt/model/lwwregister.py: -------------------------------------------------------------------------------- 1 | import hot_redis 2 | from crdt.constants import LWW_REGISTER, DATA_TYPES 3 | from crdt.redis_manager import connection 4 | from crdt.key_utilities import get_client_key, get_client_list_key 5 | 6 | 7 | class LWWERegister: 8 | def __init__(self, key): 9 | # Setting the key of the LWWERegister Instance 10 | self.key = key 11 | 12 | # Getting/Setting the client list and type of the LWWERegister instance 13 | self.client_list = hot_redis.Set(key=get_client_list_key(key), client=connection) 14 | hot_redis.Dict(key=DATA_TYPES, client=connection)[key] = LWW_REGISTER 15 | 16 | def add_client(self, client_id): 17 | # Generating a client list key for this key 18 | new_client = get_client_key(self.key, client_id) 19 | 20 | with hot_redis.transaction(): 21 | # Adding a new state to all the existing clients 22 | for client in self.client_list: 23 | hot_redis.Dict(key=client, client=connection)[new_client] = str({'timestamp': -1, 'value': None}) 24 | 25 | # Adding client to LWWERegister instance's client list 26 | self.client_list.add(new_client) 27 | 28 | # Adding a new state dictionary for this LWWERegister client 29 | new_client_state = hot_redis.Dict(key=new_client, client=connection) 30 | for client in self.client_list: 31 | new_client_state[client] = str({'timestamp': -1, 'value': None}) 32 | 33 | def set(self, client_id, val, timestamp): 34 | 35 | # Generating the current client's key for LWWERegister 36 | current_client_key = get_client_key(self.key, client_id) 37 | 38 | # Updating the client's state with the value to be set 39 | hot_redis.Dict(key=current_client_key, client=connection)[current_client_key] = str({'value': val, 'timestamp': 40 | timestamp}) 41 | 42 | def get(self, client_id): 43 | # Getting the client's key for this LWWERegister 44 | current_client_key = get_client_key(self.key, client_id) 45 | 46 | # Merging state from every other client for this LWWERegister 47 | for client in self.client_list: 48 | self.merge(current_client_key, client) 49 | 50 | # Updating the states from all other clients as the client's latest state 51 | current_client_state = hot_redis.Dict(key=current_client_key, client=connection) 52 | 53 | register = dict() 54 | register['value'] = eval(current_client_state[current_client_key])['value'] 55 | register['timestamp'] = eval(current_client_state[current_client_key])['timestamp'] 56 | for value in current_client_state.values(): 57 | value = eval(value) 58 | if value['timestamp'] > register['timestamp']: 59 | register['value'] = value['value'] 60 | register['timestamp'] = value['timestamp'] 61 | 62 | current_client_state[current_client_key] = str(register) 63 | return str(register) 64 | 65 | def merge(self, client_a, client_b): 66 | # Getting Client A and Client B's state 67 | client_a_state = hot_redis.Dict(key=client_a, client=connection) 68 | client_b_state = hot_redis.Dict(key=client_b, client=connection) 69 | 70 | # Merging Client A's state with Client B's state and storing in Client A's State 71 | for client in self.client_list: 72 | client_a_value = eval(client_a_state[client]) 73 | client_b_value = eval(client_b_state[client]) 74 | if client_a_value['timestamp'] > client_b_value['timestamp']: 75 | client_a_state[client] = str({'value': client_a_value['value'], 76 | 'timestamp': client_a_value['timestamp']}) 77 | else: 78 | client_a_state[client] = str({'value': client_b_value['value'], 79 | 'timestamp': client_b_value['timestamp']}) 80 | 81 | -------------------------------------------------------------------------------- /crdt/model/pncounter.py: -------------------------------------------------------------------------------- 1 | import hot_redis 2 | 3 | from crdt.constants import DATA_TYPES, PN_COUNTER 4 | from crdt.key_utilities import get_client_list_key, get_client_key, get_pcounter_key, get_ncounter_key 5 | from crdt.model.gcounter import GCounter 6 | from crdt.redis_manager import connection 7 | 8 | 9 | class PNCounter: 10 | def __init__(self, key): 11 | # Setting the key of the PN Counter Instance 12 | self.key = key 13 | self.pcounter = GCounter(get_pcounter_key(key)) 14 | self.ncounter = GCounter(get_ncounter_key(key)) 15 | 16 | # Getting/Setting the client list and type of the GCounter instance 17 | self.client_list = hot_redis.Set(key=get_client_list_key(key), client=connection) 18 | hot_redis.Dict(key=DATA_TYPES, client=connection)[key] = PN_COUNTER 19 | 20 | def add_client(self, client_id): 21 | # Adding client ID to client list 22 | new_client = get_client_key(self.key, client_id) 23 | self.client_list.add(new_client) 24 | 25 | # Adding clients to component GCounters 26 | self.pcounter.add_client(client_id) 27 | self.ncounter.add_client(client_id) 28 | 29 | def get(self, client_id=None): 30 | pvalue = self.pcounter.get(client_id) 31 | nvalue = self.ncounter.get(client_id) 32 | count = pvalue - nvalue 33 | return count 34 | 35 | def increment(self, client_id, inc=1): 36 | self.pcounter.increment(client_id, inc) 37 | 38 | def decrement(self, client_id, dec=1): 39 | self.ncounter.increment(client_id, dec) 40 | 41 | def set(self, client_id, pval, nval): 42 | self.pcounter.set(client_id, pval) 43 | self.ncounter.set(client_id, nval) 44 | 45 | def exists(self, client_id): 46 | if client_id in self.client_list: 47 | return True 48 | else: 49 | return False 50 | -------------------------------------------------------------------------------- /crdt/model/pnset.py: -------------------------------------------------------------------------------- 1 | import hot_redis 2 | 3 | from crdt.constants import DATA_TYPES, PN_SET 4 | from crdt.key_utilities import get_client_list_key, get_client_key, get_pncounter_item_pnset_key, get_pnset_key 5 | from crdt.model.pncounter import PNCounter 6 | from crdt.redis_manager import connection 7 | 8 | 9 | class PNSet: 10 | def __init__(self, key): 11 | self.key = key 12 | self.client_list = hot_redis.Set(key=get_client_list_key(key), client=connection) 13 | hot_redis.Dict(key=DATA_TYPES, client=connection)[key] = PN_SET 14 | self.pnset = hot_redis.Set(key=get_pnset_key, client=connection) 15 | 16 | def add_client(self, client_id): 17 | # Adding client ID to client list 18 | new_client = get_client_key(self.key, client_id) 19 | self.client_list.add(new_client) 20 | 21 | def add_client_item(self, client_id, item): 22 | pn_item_counter = PNCounter(get_pncounter_item_pnset_key(self.key, item)) 23 | if not pn_item_counter.exists(client_id): 24 | pn_item_counter.add_client(client_id) 25 | 26 | def add(self, client_id, item): 27 | self.pnset.add(item) 28 | self.add_client_item(client_id, item) 29 | PNCounter(get_pncounter_item_pnset_key(self.key, item)).increment(client_id) 30 | 31 | def remove(self, client_id, item): 32 | self.add_client_item(client_id, item) 33 | PNCounter(get_pncounter_item_pnset_key(self.key, item)).decrement(client_id) 34 | 35 | def get(self): 36 | count = set() 37 | with hot_redis.transaction(): # for consistency 38 | for item in self.pnset: 39 | if PNCounter(get_pncounter_item_pnset_key(self.key, item)).get() <= 0: 40 | self.pnset.remove(item) # Garbage Collection 41 | else: 42 | count.add(item) 43 | return count 44 | 45 | def exists(self, client_id): 46 | if client_id in self.client_list: 47 | return True 48 | else: 49 | return False 50 | -------------------------------------------------------------------------------- /crdt/model/twoptwopgraph.py: -------------------------------------------------------------------------------- 1 | import hot_redis 2 | 3 | from crdt.redis_manager import connection, DATA_TYPES 4 | from crdt.constants import TWO_P_TWO_P_GRAPH 5 | from crdt.key_utilities import get_client_list_key, get_client_key, get_nodes_set_key, get_edges_set_key 6 | from crdt.model.twopset import TwoPSet 7 | 8 | class TwoPTwoPGraph: 9 | def __init__(self, key): 10 | # Setting the key of the TwoPTwoPGraph Instance 11 | self.key = key 12 | self.nodes = TwoPSet(get_nodes_set_key(key)) 13 | self.edges = TwoPSet(get_edges_set_key(key)) 14 | 15 | # Getting/Setting the client list and type of the TwoPTwoPGraph instance 16 | self.client_list = hot_redis.Set(key=get_client_list_key(key), client=connection) 17 | hot_redis.Dict(key=DATA_TYPES, client=connection)[key] = TWO_P_TWO_P_GRAPH 18 | 19 | def add_client(self, client_id): 20 | # Adding client ID to client list 21 | new_client = get_client_key(self.key, client_id) 22 | self.client_list.add(new_client) 23 | 24 | # Adding clients to component GCounters 25 | self.nodes.add_client(client_id) 26 | self.edges.add_client(client_id) 27 | 28 | def set(self, client_id, nodes_add_set, nodes_delete_set, edges_add_set, edges_delete_set): 29 | self.nodes.set(client_id, nodes_add_set, nodes_delete_set) 30 | for edge in edges_add_set: 31 | edge_nodes = edge.split('-') 32 | if self.nodes.check(edge_nodes[0]) is False or self.nodes.check(edge_nodes[1]) is False: 33 | edges_add_set.remove(edge) 34 | self.edges.set(client_id, edges_add_set, edges_delete_set) 35 | 36 | -------------------------------------------------------------------------------- /crdt/redis_manager.py: -------------------------------------------------------------------------------- 1 | import redis 2 | import hot_redis 3 | 4 | 5 | class RedisManager: 6 | 7 | redis = None 8 | connection = None 9 | 10 | def __init__(self): 11 | # url = urlparse.urlparse(os.environ.get('REDISCLOUD_URL')) 12 | # self.redis = redis.Redis(host=url.hostname, port=url.port, password=url.password) 13 | self.redis = redis.Redis(host='localhost', port=6379) 14 | self.connection = hot_redis.HotClient(host='localhost', port=6379) 15 | 16 | 17 | redis_manager = RedisManager().redis 18 | connection = RedisManager().connection 19 | -------------------------------------------------------------------------------- /crdt/status_codes.py: -------------------------------------------------------------------------------- 1 | status_codes = { 2 | 'success': {'message': 'success', 'code': 0}, 3 | 'failure': {'message': 'failure', 'code': 1}, 4 | 'existing_key': {'message': 'failure', 'code': 2, 5 | 'description': 'An existing element with the same key found'}, 6 | 'data_type_mismatch': {'message': 'failure', 'code': 2, 7 | 'description': 'the key corresponds to another data type'}, 8 | 'missing_key_or_state': {'message': 'failure', 'code': 3, 9 | 'description': 'Key of counter was missing'}, 10 | 'key_not_found': {'message': 'failure', 'code': 4, 11 | 'description': 'Key/State of counter not found in key/value store'}, 12 | 'client_id_not_found': {'message': 'failure', 'code': 5, 13 | 'description': 'Client ID not found'}, 14 | 'existing_client_id': {'message': 'failure', 'code': 6, 15 | 'description': 'An existing client with same client id was found'}, 16 | 'data_type_mismatch': {'message': 'failure', 'code': 7, 17 | 'description': 'Key/Data Type mismatch'}, 18 | 'client_id_not_in_crdt': {'message': 'failure', 'code': 8, 19 | 'description': 'Client has not been added to CRDT instance'}, 20 | 'value_not_found': {'message': 'failure', 'code': 9, 21 | 'description': 'Value is missing'}, 22 | 'timestamp_not_valid': {'message': 'failure', 'code': 10, 23 | 'description': 'Timestamp value is not valid'}, 24 | } 25 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # ADD Documentation 2 | -------------------------------------------------------------------------------- /pip-selfcheck.json: -------------------------------------------------------------------------------- 1 | {"last_check":"2016-06-07T03:30:55Z","pypi_version":"8.1.2"} -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==0.10.1 2 | Flask-Cors==2.1.2 3 | gunicorn==19.5.0 4 | hot-redis==0.2.2 5 | itsdangerous==0.24 6 | Jinja2==2.8 7 | MarkupSafe==0.23 8 | redis==2.10.5 9 | six==1.10.0 10 | sphinx-me==0.3 11 | Werkzeug==0.11.9 12 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | import crdt 2 | 3 | crdt.run() 4 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-2.7.11 2 | -------------------------------------------------------------------------------- /tests/sampletest.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kishore-narendran/crdt-py/b3f7666b0320f0489afcb2779b0f10bb70c391d9/tests/sampletest.py --------------------------------------------------------------------------------