Metadata-Version: 2.1
Name: distrilockper
Version: 0.0.1a8
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. But get the lock without unlock is danger.

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 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(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 ')
    time.sleep(randint(10,100))

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

