Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
fopina committed Jul 14, 2020
1 parent 9041eb6 commit 040fe3f
Show file tree
Hide file tree
Showing 12 changed files with 100 additions and 397 deletions.
146 changes: 73 additions & 73 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,100 +1,100 @@
# django-bulk-update-or-create
---
title: 'django-bulk-update-or-create'
---

[![tests](https://github.com/fopina/django-database-locks/workflows/tests/badge.svg)](https://github.com/fopina/django-database-locks/actions?query=workflow%3Atests)

.. image:: https://github.com/fopina/django-database-locks/workflows/tests/badge.svg
:target: https://github.com/fopina/django-database-locks/actions?query=workflow%3Atests
:alt: tests
[![Test coverage status](https://codecov.io/gh/fopina/django-database-locks/branch/master/graph/badge.svg)](https://codecov.io/gh/fopina/django-database-locks)

.. image:: https://codecov.io/gh/fopina/django-database-locks/branch/master/graph/badge.svg
:target: https://codecov.io/gh/fopina/django-database-locks
:alt: Test coverage status
[![Current version on PyPi](https://img.shields.io/pypi/v/django-database-locks)](https://pypi.org/project/django-database-locks/)

.. image:: https://img.shields.io/pypi/v/django-database-locks
:target: https://pypi.org/project/django-database-locks/
:alt: Current version on PyPi
[![monthly downloads](https://img.shields.io/pypi/dm/django-database-locks)](https://pypi.org/project/django-database-locks/)

.. image:: https://img.shields.io/pypi/dm/django-database-locks
:target: https://pypi.org/project/django-database-locks/
:alt: monthly downloads
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/django-database-locks)

.. image:: https://img.shields.io/pypi/pyversions/django-database-locks
:alt: PyPI - Python Version

.. image:: https://img.shields.io/pypi/djversions/django-database-locks
:alt: PyPI - Django Version
![PyPI - Django Version](https://img.shields.io/pypi/djversions/django-database-locks)

Distributed locks for Django using DB (MySQL/Postgres)

Given the limitation that Percona Cluster does not support MySQL locks, this app implements locks using `select_for_update()` (row locks).
Given the limitation that Percona Cluster does not support MySQL locks,
this app implements locks using [select\_for\_update()]{.title-ref} (row
locks).

Installation
------------

pip install django-database-locks
============

> pip install django-database-locks
Usage
-----
=====

`django-database-locks` exposes one single the `lock` contextmanager and the `locked` decorator.
[django-database-locks]{.title-ref} exposes one single the
[lock]{.title-ref} contextmanager and the [locked]{.title-ref}
decorator.

The `locked` decorator will wrap a django management command (subclasses of `django.core.management.base.BaseCommand`) or any function with the `lock` contextmanager:
The [locked]{.title-ref} decorator will wrap a django management command
(subclasses of [django.core.management.base.BaseCommand]{.title-ref}) or
any function with the [lock]{.title-ref} contextmanager:

``` {.python}
from django.core.management.base import BaseCommand
.. code-block:: python
from database_locks import locked
from django.core.management.base import BaseCommand
@locked
class Command(BaseCommand):
...
def handle(self, *args, **options):
self.stdout.write('Got the lock')
```

from database_locks import locked
``` {.python}
from database_locks import locked
@locked
class Command(BaseCommand):
...
def handle(self, *args, **options):
self.stdout.write('Got the lock')
class SomeClass:
def non_locked(self):
pass
@locked
def locked(self):
print('got lock')
```

.. code-block:: python
``` {.python}
from database_locks import lock
from database_locks import locked

class SomeClass:
def non_locked(self):
pass
@locked
def locked(self):
print('got lock')
class SomeClass:
def non_locked(self):
pass
.. code-block:: python

from database_locks import lock

class SomeClass:
def non_locked(self):
pass
def locked(self):
with lock():
print('got lock')
def locked(self):
with lock():
print('got lock')
```

Docs
----

Both `lock` and `locked` have the same optional args:

.. code-block:: python

:param lock_name: unique name in DB for this function
:param timeout: numbers of seconds to wait to acquire lock
:param lock_ttl: expiration timer of the lock, in seconds (set to None to infinite)
:param locked_by: owner id for the lock (if lock is active but owner is the same, returns acquired)
:param auto_renew: if set to True will re-acquire lock (for `lock_ttl` seconds) before `lock_ttl` is over.
auto_renew thread will raise KeyboardInterrupt on the main thread in case re-acquiring fails
:param retry: retry every `retry` seconds acquiring until successful. set to None or 0 to disable.
:param lost_lock_cb: callback function when lock is lost (when re-acquiring). defaults to raising LockException

There are also the following options you can specify in the project `settings.py`

- *DATABASE_LOCKS_STATUS_FILE*: file that will be updated with the lock status (default `None`). Useful when you have multiple shared-lock processes, to quickly inspect which one has the lock.
- *DATABASE_LOCKS_ENABLED*: set to `False` to globally disable locks (default `True`)
====

Both [lock]{.title-ref} and [locked]{.title-ref} have the same optional
args:

``` {.python}
:param lock_name: unique name in DB for this function
:param timeout: numbers of seconds to wait to acquire lock
:param lock_ttl: expiration timer of the lock, in seconds (set to None to infinite)
:param locked_by: owner id for the lock (if lock is active but owner is the same, returns acquired)
:param auto_renew: if set to True will re-acquire lock (for `lock_ttl` seconds) before `lock_ttl` is over.
auto_renew thread will raise KeyboardInterrupt on the main thread in case re-acquiring fails
:param retry: retry every `retry` seconds acquiring until successful. set to None or 0 to disable.
:param lost_lock_cb: callback function when lock is lost (when re-acquiring). defaults to raising LockException
```

There are also the following options you can specify in the project
[settings.py]{.title-ref}

- *DATABASE\_LOCKS\_STATUS\_FILE*: file that will be updated with the
lock status (default [None]{.title-ref}). Useful when you have
multiple shared-lock processes, to quickly inspect which one has the
lock.
- *DATABASE\_LOCKS\_ENABLED*: set to [False]{.title-ref} to globally
disable locks (default [True]{.title-ref})
4 changes: 1 addition & 3 deletions bulk_update_or_create/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
from .locks import locked, lock

default_app_config = 'database_locks.apps.DBLocksConfig'
default_app_config = 'bulk_update_or_create.apps.BulkUpdateOrCreateConfig'
13 changes: 2 additions & 11 deletions bulk_update_or_create/apps.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
from django.apps import AppConfig
from django.conf import settings

APP_SETTINGS = dict(STATUS_FILE=None, ENABLED=True)


class DBLocksConfig(AppConfig):
name = 'database_locks'

def ready(self):
for k, v in APP_SETTINGS.items():
_k = 'DATABASE_LOCKS_%s' % k
if not hasattr(settings, _k):
setattr(settings, _k, v)
class BulkUpdateOrCreateConfig(AppConfig):
name = 'bulk_update_or_create'
Loading

0 comments on commit 040fe3f

Please sign in to comment.