Metadata-Version: 2.1
Name: distrilockper
Version: 0.0.1a10
Summary: Distributed Lock with using Redis
Home-page: https://gitlab.com/luislui/distributed_lock_helper
Author: Louis Lui
Author-email: luis11235178@gmail.com
License: MIT License
Keywords: Lock,Distributed
Platform: UNKNOWN
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Topic :: Database
Description-Content-Type: text/markdown
Requires-Dist: coloredlogs (~=14.0)
Requires-Dist: uuid (~=1.30)
Requires-Dist: redis (~=3.0.1)
Requires-Dist: python-json-logger
Requires-Dist: redis-py-cluster
Requires-Dist: atomos
Requires-Dist: nest-asyncio
Requires-Dist: multipledispatch
Requires-Dist: pebble


# distrilockper - Distributed Lock Helper

### Feature

- Support Redis Cluster and Redis Single setup
- Redis operation with Lua scripting (Atomic operations)
- Lock watch dog (auto increase the alive time of key if the process time is large than key ttl)
- Thread safe
- Support Reentrant lock
- Support expired unlock function, automatically unlock after x seconds, no need to manually unlock by call unlock method
- Support try lock, wait n second if the lock is existed.

### Basic Usage

1. Install 

    ```python
    pip install distrilockper
    ```

2. declare config instance

    ```python
    from distrilockper import Config
    config = Config()
    ```

3. select single Redis server mode or cluster Redis servers mode

    ```python
    config.use_single_server()

    ```

    ```python
    config.use_cluster_servers()
    ```

4. set the config

    ```python
    config.use_single_server().set_config(host='0.0.0.0', port=6379)
    ```

    ```python
    config.use_cluster_servers().set_config(host='0.0.0.0', port=7000)
    ```

    ```python
    config.use_cluster_servers().add_node_address(host='0.0.0.0', port=7000) \
                                    .add_node_address(host='0.0.0.0', port=7001) \
                                    .add_node_address(host='0.0.0.0', port=7002) \
                                    .add_node_address(host='0.0.0.0', port=7003) \
                                    .add_node_address(host='0.0.0.0', port=7004) \
                                    .add_node_address(host='0.0.0.0', port=7005)
    ```

    The set_config and add_node_address method takes several arguments from python redis library

5. declare the lock instance

    ```python
    helper = LockHelper()
    helper.create(config)
    ```

6. get a kind of lock

    ```python
    lock = helper.get_reentrant_lock(key='apples')
    ```

7. try lock the key

    ```python
    result = lock.try_lock(wait_time=10,lease_time=7,time_unit='second')
    ```

    The try_lock method takes several arguments:

    - `wait_time` : try lock operation time out
    - `lease_time` : the release time of the lock
    - `time_unit` : unit of lease_time and wait_time
        - `seconds` / `s`
        - `hour` / `h`
        - `minute` / `m`
        - `milliseconds`/ `ms`

8. unlock after business logic done

    ```python
    lock.unlock()
    ```

## Reentrant

the reentrant lock only supports in the same thread

### Get the lock in different thread

```python
from distrilockper import Config
from distrilockper import LockHelper
from multiprocessing.dummy import Pool as ThreadPool

config = Config()
config.use_single_server().set_config(host='0.0.0.0', port=6379)

helper = LockHelper()
helper.create(config)

def get_lock(_):
    print("run", _)
    Locker1 = helper.get_reentrant_lock(key='apples')
    re1 = Locker1.try_lock(60, 10, 'second')
    assert re1 == True
    print("get lock",re1)
    assert Locker1.is_exists() == True
    print('exists', Locker1.is_exists())

pool = ThreadPool(100)
results = pool.map(get_lock, range(10))
```

### get the lock in same thread

```python
from distrilockper import Config
from distrilockper import LockHelper
from multiprocessing.dummy import Pool as ThreadPool

config = Config()
config.use_single_server().set_config(host='0.0.0.0', port=6379)

helper = LockHelper()
helper.create(config)

for i in range(10):
    Locker1 = helper.get_reentrant_lock(key='apples')
    re1 = Locker1.try_lock(60, 10, 'second')
    assert re1 == True
    print("get lock", re1)
    assert Locker1.is_exists() == True
    print('exists', Locker1.is_exists())
```

## Watchdog (for lock)

use case: the time is not predictable for time-consuming task or business logic. you can not set the fixed release time for lock you got. Alternative you can set a high number for release but it will be wait a long long time when you program be aborted unexpectedly

Watchdog mechanism will refresh regularly the lock until you call unlock method or the program be aborted unexpectedly

```python
from distrilockper import Config
from distrilockper import LockHelper
from random import randint
from time import sleep

config = Config()
config.use_single_server().set_config(host='0.0.0.0', port=6379)

helper = LockHelper()
helper.create(config)

for i in range(10):
    Locker1 = helper.get_reentrant_lock(key='apples')
    re1 = Locker1.try_lock(wait_time=60, time_unit= 'second')
    assert re1 == True
    print("get lock", re1)
    assert Locker1.is_exists() == True
    print('exists', Locker1.is_exists())

    print('do something ')
    sleep(randint(10,100))

    assert Locker1.unlock() == True
```

