django-dbsemaphore


Namedjango-dbsemaphore JSON
Version 0.1.1 PyPI version JSON
download
home_pagehttps://hub.sr.ht/~nullenenenen/django-dbsemaphore/
Summarydjango-dbsemaphore — multi-ticket semaphores implemented on top of DB row locks
upload_time2023-05-12 13:12:43
maintainer
docs_urlNone
authornullenenenen
requires_python>=3.8
licenseLGPL-3.0+
keywords django semaphore
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Django-DBSemaphore

This gives you multi-ticket DB-defined semaphores implemented on top of DB row locks.

# Alternatives

The orthodox alternatives are:
- Posix semaphores (eg using [posix_ipc](https://pypi.org/project/posix-ipc/))
- SysV semaphores (eg using [sysv-ipc](https://pypi.org/project/sysv-ipc/))

## Why not use those then?
If a process which holds a Posix or SysV mutex ticket crashes while holding the ticket, it can't return it to the semaphore.
Thus, you can leak tickets.
But if a process which holds a ticket from Django-DBSemaphore crashes, its database connection goes with it, which terminates the DB transaction and frees the ticket. Crashing processes don't leak tickets.

## And why not a lockfile?
Locked files (or locked byte ranges) disappear when a process crashes; its file descriptors are gone thus so are its locks.
That's a great property. However, file locks are not multi-ticket. The file (or byterange therein) is either locked or not.
The OS file locking APIs are good to implement *mutexes* with, but not *semaphores* — except, trivially, a 1-ticket semaphore — which is a mutex ;-).

# Quirks
- In contrast to Posix/SysV semaphores and `lockf`-based approaches, with django-dbsemaphore you can't block until a ticket becomes available.
- From within the same transaction you can acquire tickets you already have over and over. In fact, it's currently impossible to get new tickets of a semaphore on a transaction that already has a ticket of that same semaphore. Typically, you won't need multiple tickets of the same semaphore in the same transaction, but a future version of this software might make it possible. In the meantime, consider using multiple semaphores for your multistage semaphore needs.
- At the base level, they work within transactions. If you want to use `dbsemaphore.semaphore.acquire()`, you'll need to structure your ticket acquisitions around DB transactions, and close those transactions (rollback or commit) to return the tickets. However, there is a context manager (`dbsemaphore.contextmanager.semaphore_ticket()`) that abstracts all of that away for you and makes it easy to use a ticket from anywhere in your code. See below.

# Compatibility
Currently this is tested on PostgreSQL 14 and Django 3.2. But it is known to work with Django 2.2.
- It currently doesn't work on SQLite due to the way in which tables are locked in `semaphore.make()`
- MySQL, Oracle: Untested.
- (neat) Patches welcome!

# Installing
1. `pip install django-dbsemaphore`
2. add 'dbsemaphore' to your Django's `settings.INSTALLED_APPS`.
3. run `./manage.py migrate dbsemaphore` or some variation of such

# How to use it

Have a look at the below examples, run the Django test, or read `test.py`.


## Semaphore management

```python
from dbsemaphore import semaphore as sem

# Creates a semaphore called 'test' with 3 tickets
>>> sem.make('test', 3)

# Increases the number of tickets of semaphore 'test' to 4.
# Blocks on concurrent calls of `make`.
# If 'test' doesn't exist, it will be created (with 4 tickets).
>>> sem.make('test', 4)

# Decreases the number of tickets of semaphore 'test' to 2.
# This can block, in the worst case until all tickets have been returned.
# As `make` calls block on eachother, this thus also blocks any *increase* of tickets until this decrease has succeeded.
>>> sem.make('test', 2)

# Returns a dictionary of available semaphores, with their ticket counts.
>>> sem.list()
{'test', 2}

# Destroys the semaphore. Blocks until all its tickets have been returned.
>>> sem.destroy('test')
```

## Acquiring tickets; what we're here for!

### With the contextmanager

```python
from dbsemaphore.contextmanager import semaphore_ticket

# We use the semaphore named 'test' that we have created above.
with semaphore_ticket('test') as theticket:
    if theticket is None:
        print("Boo! No ticket was available!")
    else:
        do_the_ticketed_thing()
```

### Using the lower-level API

```python
from django.db import transaction
from dbsemaphore import semaphore as sem

@transaction.atomic
def do_something_potentially_from_many_processes_or_threads_but_not_too_many_at_the_same_time():
    # We use the semaphore named 'test' that we have created above.
    if ticket := sem.acquire('test'):
        do_that_something()

# When the transaction terminates, the ticket is returned to the semaphore.
# In fact, there isn't any API function to explicitly return a ticket...
```

            

Raw data

            {
    "_id": null,
    "home_page": "https://hub.sr.ht/~nullenenenen/django-dbsemaphore/",
    "name": "django-dbsemaphore",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "",
    "keywords": "Django semaphore",
    "author": "nullenenenen",
    "author_email": "nullenenenen@gavagai.eu",
    "download_url": "https://files.pythonhosted.org/packages/cb/e9/76b11c491b2d0addc23f4c8a37933fef2d20057f4302c128ffb5de54a19d/django-dbsemaphore-0.1.1.tar.gz",
    "platform": null,
    "description": "# Django-DBSemaphore\n\nThis gives you multi-ticket DB-defined semaphores implemented on top of DB row locks.\n\n# Alternatives\n\nThe orthodox alternatives are:\n- Posix semaphores (eg using [posix_ipc](https://pypi.org/project/posix-ipc/))\n- SysV semaphores (eg using [sysv-ipc](https://pypi.org/project/sysv-ipc/))\n\n## Why not use those then?\nIf a process which holds a Posix or SysV mutex ticket crashes while holding the ticket, it can't return it to the semaphore.\nThus, you can leak tickets.\nBut if a process which holds a ticket from Django-DBSemaphore crashes, its database connection goes with it, which terminates the DB transaction and frees the ticket. Crashing processes don't leak tickets.\n\n## And why not a lockfile?\nLocked files (or locked byte ranges) disappear when a process crashes; its file descriptors are gone thus so are its locks.\nThat's a great property. However, file locks are not multi-ticket. The file (or byterange therein) is either locked or not.\nThe OS file locking APIs are good to implement *mutexes* with, but not *semaphores* \u2014 except, trivially, a 1-ticket semaphore \u2014 which is a mutex ;-).\n\n# Quirks\n- In contrast to Posix/SysV semaphores and `lockf`-based approaches, with django-dbsemaphore you can't block until a ticket becomes available.\n- From within the same transaction you can acquire tickets you already have over and over. In fact, it's currently impossible to get new tickets of a semaphore on a transaction that already has a ticket of that same semaphore. Typically, you won't need multiple tickets of the same semaphore in the same transaction, but a future version of this software might make it possible. In the meantime, consider using multiple semaphores for your multistage semaphore needs.\n- At the base level, they work within transactions. If you want to use `dbsemaphore.semaphore.acquire()`, you'll need to structure your ticket acquisitions around DB transactions, and close those transactions (rollback or commit) to return the tickets. However, there is a context manager (`dbsemaphore.contextmanager.semaphore_ticket()`) that abstracts all of that away for you and makes it easy to use a ticket from anywhere in your code. See below.\n\n# Compatibility\nCurrently this is tested on PostgreSQL 14 and Django 3.2. But it is known to work with Django 2.2.\n- It currently doesn't work on SQLite due to the way in which tables are locked in `semaphore.make()`\n- MySQL, Oracle: Untested.\n- (neat) Patches welcome!\n\n# Installing\n1. `pip install django-dbsemaphore`\n2. add 'dbsemaphore' to your Django's `settings.INSTALLED_APPS`.\n3. run `./manage.py migrate dbsemaphore` or some variation of such\n\n# How to use it\n\nHave a look at the below examples, run the Django test, or read `test.py`.\n\n\n## Semaphore management\n\n```python\nfrom dbsemaphore import semaphore as sem\n\n# Creates a semaphore called 'test' with 3 tickets\n>>> sem.make('test', 3)\n\n# Increases the number of tickets of semaphore 'test' to 4.\n# Blocks on concurrent calls of `make`.\n# If 'test' doesn't exist, it will be created (with 4 tickets).\n>>> sem.make('test', 4)\n\n# Decreases the number of tickets of semaphore 'test' to 2.\n# This can block, in the worst case until all tickets have been returned.\n# As `make` calls block on eachother, this thus also blocks any *increase* of tickets until this decrease has succeeded.\n>>> sem.make('test', 2)\n\n# Returns a dictionary of available semaphores, with their ticket counts.\n>>> sem.list()\n{'test', 2}\n\n# Destroys the semaphore. Blocks until all its tickets have been returned.\n>>> sem.destroy('test')\n```\n\n## Acquiring tickets; what we're here for!\n\n### With the contextmanager\n\n```python\nfrom dbsemaphore.contextmanager import semaphore_ticket\n\n# We use the semaphore named 'test' that we have created above.\nwith semaphore_ticket('test') as theticket:\n    if theticket is None:\n        print(\"Boo! No ticket was available!\")\n    else:\n        do_the_ticketed_thing()\n```\n\n### Using the lower-level API\n\n```python\nfrom django.db import transaction\nfrom dbsemaphore import semaphore as sem\n\n@transaction.atomic\ndef do_something_potentially_from_many_processes_or_threads_but_not_too_many_at_the_same_time():\n    # We use the semaphore named 'test' that we have created above.\n    if ticket := sem.acquire('test'):\n        do_that_something()\n\n# When the transaction terminates, the ticket is returned to the semaphore.\n# In fact, there isn't any API function to explicitly return a ticket...\n```\n",
    "bugtrack_url": null,
    "license": "LGPL-3.0+",
    "summary": "django-dbsemaphore \u2014 multi-ticket semaphores implemented on top of DB row locks",
    "version": "0.1.1",
    "project_urls": {
        "Documentation": "https://git.sr.ht/~nullenenenen/django-dbsemaphore/tree/master/item/README.md",
        "Homepage": "https://hub.sr.ht/~nullenenenen/django-dbsemaphore/",
        "Source": "https://git.sr.ht/~nullenenenen/django-dbsemaphore/"
    },
    "split_keywords": [
        "django",
        "semaphore"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "347345461d17f5a44880b1e9c9c78d2056502a655de62d7c63d898f35297f558",
                "md5": "e4f7b0948d72a2abcc91ebbdda67c56b",
                "sha256": "a3cc53da7e28c29bbb043e9d19ad85e2f3a3ac77e4c9b5f73483c5b3e7288f65"
            },
            "downloads": -1,
            "filename": "django_dbsemaphore-0.1.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "e4f7b0948d72a2abcc91ebbdda67c56b",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 11609,
            "upload_time": "2023-05-12T13:12:41",
            "upload_time_iso_8601": "2023-05-12T13:12:41.384098Z",
            "url": "https://files.pythonhosted.org/packages/34/73/45461d17f5a44880b1e9c9c78d2056502a655de62d7c63d898f35297f558/django_dbsemaphore-0.1.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "cbe976b11c491b2d0addc23f4c8a37933fef2d20057f4302c128ffb5de54a19d",
                "md5": "fe845dbdf604071e4e7e922e52145c1c",
                "sha256": "0c7ceb0de8236bbfc22406a02e018a742b481928bd290366a564cf6789b6b1eb"
            },
            "downloads": -1,
            "filename": "django-dbsemaphore-0.1.1.tar.gz",
            "has_sig": false,
            "md5_digest": "fe845dbdf604071e4e7e922e52145c1c",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 11827,
            "upload_time": "2023-05-12T13:12:43",
            "upload_time_iso_8601": "2023-05-12T13:12:43.119458Z",
            "url": "https://files.pythonhosted.org/packages/cb/e9/76b11c491b2d0addc23f4c8a37933fef2d20057f4302c128ffb5de54a19d/django-dbsemaphore-0.1.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-05-12 13:12:43",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "django-dbsemaphore"
}
        
Elapsed time: 0.13100s