Giter Club home page Giter Club logo

django-sequences's Introduction

django-sequences

By default, Django gives each model an auto-incrementing integer primary key. These primary keys look like they generate a continuous sequence of integers.

However, this behavior isn't guaranteed.

If a transaction inserts a row and then is rolled back, the sequence counter isn't rolled back for performance reasons, creating a gap in primary keys.

Such gaps may happen on all databases natively supported by Django:

They may also happen on most databases supported via third-party backends.

This can cause compliance issues for some use cases such as accounting.

This risk isn't well known. Since most transactions succeed, values look sequential. Gaps will only be revealed by audits.

django-sequences solves this problem with a get_next_value function designed to be used as follows:

from django.db import transaction
from sequences import get_next_value
from invoices.models import Invoice

with transaction.atomic():
    Invoice.objects.create(number=get_next_value("invoice_numbers"))

Or, if you'd rather use an object-oriented API:

from django.db import transaction
from sequences import Sequence
from invoices.models import Invoice

invoice_numbers = Sequence("invoice_numbers")

with transaction.atomic():
    Invoice.objects.create(number=next(invoice_numbers))

get_next_value relies on the database's transactional integrity to ensure that each value is returned exactly once. As a consequence, the guarantees of django-sequences apply only if you call get_next_value and save its return value to the database within the same transaction!

Table of contents

Getting started

django-sequences is tested with Django Django 3.2 (LTS), 4.0, 4.1, 4.2 (LTS), and 5.0.

It is also tested with all database backends built-in to Django: MySQL/MariaDB, Oracle, PostgreSQL and SQLite.

It is released under the BSD license, like Django itself.

Install django-sequences:

$ pip install django-sequences

Add it to the list of applications in your project's settings:

INSTALLED_APPS = [
    ...,
    "sequences.apps.SequencesConfig",
    ...
]

Run migrations:

$ django-admin migrate

API

get_next_value

>>> from sequences import get_next_value

This function generates a gapless sequence of integer values:

>>> get_next_value()
1
>>> get_next_value()
2
>>> get_next_value()
3

It supports multiple independent sequences:

>>> get_next_value("cases")
1
>>> get_next_value("cases")
2
>>> get_next_value("invoices")
1
>>> get_next_value("invoices")
2

The first value defaults to 1. It can be customized:

>>> get_next_value("customers", initial_value=1000)  # pro growth hacking

The initial_value parameter only matters when get_next_value is called for the first time for a given sequence — assuming the corresponding database transaction gets committed; as discussed above, if the transaction is rolled back, the generated value isn't consumed. It's also possible to initialize a sequence in a data migration and not use initial_value in actual code.

Sequences can loop:

>>> get_next_value("seconds", initial_value=0, reset_value=60)

When the sequence reaches reset_value, it restarts at initial_value. In other words, it generates reset_value - 2, reset_value - 1, initial_value, initial_value + 1, etc. In that case, each call to get_next_value must provide initial_value when it isn't the default and reset_value.

Database transactions that call get_next_value for a given sequence are serialized. As a consequence, when you call get_next_value in a database transaction, other callers trying to get a value from the same sequence block until the transaction completes, either with a commit or a rollback. You should keep such transactions short to minimize the impact on performance.

This is why databases default to a faster behavior that may create gaps.

Passing nowait=True makes get_next_value raise an exception instead of blocking in this scenario. This is rarely useful. Also it doesn't work for the first call. (This is a bug but it's harmless and hard to fix.)

Calls to get_next_value for distinct sequences don't interact with one another.

Finally, passing using="..." allows selecting the database on which the current sequence value is stored. When this parameter isn't provided, it defaults to the default database for writing models of the sequences application. See Multiple databases for details.

To sum up, the complete signature of get_next_value is:

get_next_value(
    sequence_name="default",
    initial_value=1,
    reset_value=None,
    *,
    nowait=False,
    using=None,
)

get_next_values

>>> from sequences import get_next_values

This function generates values in batch:

>>> get_next_values(10)
range(1, 11)
>>> get_next_values(10)
range(11, 21)

As a reminder, you must save all these values to the database within the same transaction to benefit from the guarantees of django-sequences.

get_next_values supports the same arguments as get_next_value except reset_value.

The complete signature of get_next_values is:

get_next_values(
    batch_size,
    sequence_name="default",
    initial_value=1,
    *,
    nowait=False,
    using=None,
)

get_last_value

>>> from sequences import get_last_value

This function returns the last value generated by a sequence:

>>> get_last_value()
None
>>> get_next_value()
1
>>> get_last_value()
1
>>> get_next_value()
2
>>> get_last_value()
2

If the sequence hasn't generated a value yet, get_last_value returns None.

It supports independent sequences like get_next_value:

>>> get_next_value("cases")
1
>>> get_last_value("cases")
1
>>> get_next_value("invoices")
1
>>> get_last_value("invoices")
1

It accepts using="..." for selecting the database on which the current sequence value is stored, defaulting to the default database for reading models of the sequences application.

The complete signature of get_last_value is:

get_last_value(
    sequence_name="default",
    *,
    using=None,
)

get_last_value is a convenient and fast way to tell how many values a sequence generated but it makes no guarantees. Concurrent calls to get_next_value may produce unexpected results of get_last_value.

delete

>>> from sequences import delete

This function deletes a sequence. It returns True if the sequence existed and was deleted and False otherwise.

>>> company_id = "b1f6cdef-367f-49e4-9cf5-bb0d34707af8"
>>> get_next_value(f"invoices—{company_id}")
1
>>> delete(f"invoices—{company_id}")
True
>>> delete(f"invoices—{company_id}")
False

It accepts using="..." for selecting the database, like get_next_value.

The complete signature of delete is:

delete(
    sequence_name="default",
    *,
    using=None,
)

delete is useful when you create many sequences and want to dispose of some.

Sequence

>>> from sequences import Sequence

(not to be confused with sequences.models.Sequence, a private API)

This class stores parameters for a sequence and provides get_next_value, get_next_values, get_last_value, and delete methods:

>>> claim_ids = Sequence("claims")
>>> claim_ids.get_next_value()
1
>>> claim_ids.get_next_value()
2
>>> claim_ids.get_last_value()
2
>>> claim_ids.delete()
True

This reduces the risk of errors when the same sequence is used in multiple places.

Instances of Sequence are also infinite iterators:

>>> next(claim_ids)
3
>>> next(claim_ids)
4

The complete API is:

Sequence(
    sequence_name="default",
    initial_value=1,
    reset_value=None,
    *,
    using=None,
)

Sequence.get_next_value(
    self,
    *,
    nowait=False,
)

Sequence.get_next_values(
    self,
    batch_size,
    *,
    nowait=False,
)

Sequence.get_last_value(
    self,
)

Sequence.delete(
    self,
)

All parameters have the same meaning as in the get_next_value, get_last_values, get_last_value, and delete functions.

Examples

Per-date sequences

If you want independent sequences per day, month, or year, use the appropriate date fragment in the sequence name. For example:

from django.utils import timezone
from sequences import get_next_value

# Per-day sequence
get_next_value(f"books-{timezone.now().date().isoformat()}")
# Per-year sequence
get_next_value(f"prototocol-{timezone.now().year}")

The above calls will result in separate sequences like books-2023-03-15 or protocol-2024, respectively.

Database support

django-sequences is tested on PostgreSQL, MariaDB / MySQL, Oracle, and SQLite.

MySQL only supports the nowait parameter from version 8.0.1. MariaDB only supports nowait from version 10.3.

Multiple databases

Since django-sequences relies on the database to guarantee transactional integrity, the current value for a given sequence must be stored in the same database as models containing generated values.

In a project that uses multiple databases, you must write a suitable database router to create tables for the sequences application on all databases storing models containing sequential numbers.

Each database has its own namespace: a sequence with the same name stored in two databases will have independent counters in each database.

Isolation levels

Since django-sequences relies on the database's transactional integrity, using a non-default transaction isolation level requires special care.

  • read uncommitted: django-sequences cannot work at this isolation level.

    Indeed, concurrent transactions can create gaps, as in this scenario:

    • Transaction A reads N and writes N + 1;
    • Transaction B reads N + 1 (dirty read) and writes N + 2;
    • Transaction A is rolled back;
    • Transaction B is committed;
    • N + 1 is a gap.

    The read uncommitted isolation level doesn't provide sufficient guarantees. It will never be supported.

  • read committed: django-sequences works best at this isolation level, like Django itself.

  • repeatable read: django-sequences also works at this isolation level, provided your code handles serialization failures and retries transactions.

    This requirement isn't specific to django-sequences. It's generally needed when running at the repeatable read isolation level.

    Here's a scenario where only one of two concurrent transactions can complete on PostgreSQL:

    • Transaction A reads N and writes N + 1;
    • Transaction B attemps to read; it must wait until transaction A completes;
    • Transaction A is committed;
    • Transaction B is aborted.

    On PostgreSQL, serialization failures are reported as: OperationalError: could not serialize access due to concurrent update.

    On MySQL, they result in: OperationalError: (1213, 'Deadlock found when trying to get lock; try restarting transaction').

    Concurrent transactions initializing the same sequence are also vulnerable, although that's hardly ever a problem in practice.

    On PostgreSQL, this manifests as IntegrityError: duplicate key value violates unique constraint "sequences_sequence_pkey".

  • serializable: the situation is identical to the repeatable read level.

    SQLite always runs at the serializable isolation level. Serialization failures result in: OperationalError: database is locked.

Contributing

Prepare a development environment:

  • Install Poetry.
  • Run poetry install.
  • Run poetry shell to load the development environment.

Prepare testing databases:

  • Install PostgreSQL, MariaDB, and Oracle.
  • Create a database called sequences, owned by a user called sequences with password sequences, with permissions to create a test_sequences test database. You may override these values with environment variables; see tests/*_settings.py for details.

Make changes:

  • Make changes to the code, tests, or docs.
  • Run make style and fix errors.
  • Run make test to run the set suite on all databases.

Iterate until you're happy.

Check quality and submit your changes:

  • Install tox.
  • Run tox to test on all Python and Django versions and all databases.
  • Submit a pull request.

Releasing

Increment version number X.Y in pyproject.toml.

Commit, tag, and push the change:

$ git commit -m "Bump version number".
$ git tag X.Y
$ git push
$ git push --tags

Build and publish the new version:

$ poetry build
$ poetry publish

Changelog

3.0

  • Add the get_next_values function for generating values in batch.

2.9

  • Add the delete function.

2.8

  • No significant changes.

2.7

  • Sequence values can go up to 2 ** 63 - 1 instead of 2 ** 31 - 1 previously. The exact limit depends on the database backend.

    Migration 0002_alter_sequence_last.py changes the field storing sequence values from PositiveIntegerField to PositiveBigIntegerField. Running it requires an exclusive lock on the table, which prevents other operations, including reads.

    If you have many distinct sequences, e.g. if you create one sequence per user and you have millions of users, review how the migration will affect your app before running it or skip it with migrate --fake.

2.6

  • Improve documentation.

2.5

  • Fix Japanese and Turkish translations.
  • Restore compatibility with Python 3.5.
  • Support relabeling the sequences app with a custom AppConfig.

2.4

  • Add the get_last_value function.
  • Add the Sequence class.

2.3

  • Optimize performance on MySQL.
  • Test on MySQL, SQLite and Oracle.

2.2

  • Optimize performance on PostgreSQL ≥ 9.5.

2.1

  • Provide looping sequences with reset_value.

2.0

  • Add support for multiple databases.
  • Add translations.
  • nowait becomes keyword-only argument.
  • Drop support for Python 2.

1.0

  • Initial stable release.

django-sequences's People

Contributors

aaugustin avatar adamchainz avatar bugraaydin avatar hvdklauw avatar rmoch avatar savvan0h avatar spapas avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-sequences's Issues

Atomic block behaviour

This would be a question actually, but it will clarify the documentation for me, so I would be grateful if you could elaborate on this. What would be the assumed behaviour in the following scenario:

with transaction.atomic():
    # Code A
    with transaction.atomic():
        x = get_next_value('test')
        # Code B
    # Code C
  1. Thread is invoking Code B instructions - are other threads blocked waiting (on which line?) until it ends to run Code B? What about Code A?
  2. Thread is invoking Code C instructions. Are other threads blocked waiting (on which line?) until it ends to run Code C? Code B? Code A?

I know the library guarantees than get_next_value always return concurrent gapless values. But for example if I assign this value to an Invoice number in Code B and something goes wrong, there may be a chance that the next transaction would get the next number (in case execution of Code B does not block other threads).

Thank in advance for any answer.

Initial Django Migration Issue

Hi there, I performed a database dump using pg_dump with version 2.7 installed however the new migration file not applied, upon moving to a new instance of my app and restoring the db, the initial migration pops up with:

Running migration against app-debug
Traceback (most recent call last):
  File "/app/wholesale/manage.py", line 23, in <module>
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.11/site-packages/django/core/management/__init__.py", line 446, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.11/site-packages/django/core/management/__init__.py", line 440, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.11/site-packages/django/core/management/base.py", line 402, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.11/site-packages/django/core/management/base.py", line 448, in execute
    output = self.handle(*args, **options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/core/management/base.py", line 96, in wrapped
    res = handle_func(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/core/management/commands/migrate.py", line 117, in handle
    executor.loader.check_consistent_history(connection)
  File "/usr/local/lib/python3.11/site-packages/django/db/migrations/loader.py", line 327, in check_consistent_history
    raise InconsistentMigrationHistory(
django.db.migrations.exceptions.InconsistentMigrationHistory: Migration table.0025_auto_20190212_1447 is applied before its dependency sequences.0002_alter_sequence_last on database 'default'.

In order to get around this, I reverted back to 2.6 and performed the initial migration, in order to use the new version/migration file is there something I need to do? or is this only if you apply the migrations to a new project? and not one with existing positiveIntegerFields?

About daily sequence

Hello, I would like to generate a sequence which renews everyday. Is there any function to do so?
e.g. yyyymmddNN. where NN will start from 01 for each date. Thanks.

Deprecation warning: django.utils.translation.ugettext_lazy() is deprecated

When running tests with deprecation warnings, I see the following warning:

python -Wa ./manage.py test 
site-packages/sequences/models.py:19: RemovedInDjango40Warning: django.utils.translation.ugettext_lazy() is deprecated in favor of django.utils.translation.gettext_lazy().
  verbose_name_plural = _("sequences")

Thanks for creating and maintaining this package.

Timeouts on Parallel Requests

Hi,
We're using django 1.10 with Postgresql 9.2 and python 3.5
We have decided to use your package since we need field auto-incremental that is not PK (for some model), and it should be unique per other model.
The sequences table is within our models DB.

It seemed like the solution was good, until we started getting parallel requests. In that case - looks like the DB got locked for too long and we started getting timeouts (We are configured to 10 seconds timeout by our api gateway (Apigee)

Is that behavior expected or the package should handle those cases?
Thanks.

Not support for python 2?

When using it in python2.7 enviroment. met this problem:
File "/usr/local/lib/python2.7/dist-packages/sequences/__init__.py", line 6 *, nowait=False, using=None): ^ SyntaxError: invalid syntax

dropping old sequences

Currently to drop outdated/unwanted sequences from the database, you need to use the private api.
(eg via orm operations on sequences.models.Sequence)

It would be nice if this functionality was exposed in the public api.

Rename master branch to main

@adamchainz - creating this issue as a heads-up for you.

To update your local copy, GitHub recommends:

git branch -m master main
git fetch origin
git branch -u origin/main main
git remote set-head origin -a

Please close it when you've seen it!

Separate sequence property specifiers

When using

get_next_value("orders", initial_value=100, reset_value=500)

I can easily tell what this function does and what values to expect, but if this sequence name was used in multiple files as

get_next_value("orders")

it wouldn't be obvious or it would be hard to figure out what the initial_value or the rest_value is.

My proposal is doing one of:

  1. allowing a declarative class usage:
orders_sequence = DjangoSequence("orders", initial_value=100, reset_value=500)

get_next_value(orders_sequence)
  1. enum-like usage:
class DjangoSequences:
    ORDERS = DjangoSequence("orders", initial_value=100, reset_value=500)

get_next_value(DjangoSequences.ORDERS)

My current usage is like the following:

orders_sequence = {"sequence_name": "orders", "initial_value": 100, "reset_value": 500}

get_next_value(**orders_sequence)

New release

Can you please make a new release so we can get rid of the deprecation warning fixed in 631b00d ?

Thanks.

Document usage with different isolation levels

I was getting OperationalError: could not serialize access due to concurrent update when I was testing two concurrent requests with django-sequences. Now I realized, that I get those only if I have set 'isolation_level': ISOLATION_LEVEL_REPEATABLE_READ.

I would appreciate, if usage of django-sequences with different isolation levels was discussed in the documentation.

Help elaborate the background of the problem?

Hi, this is a great extension and I love it. Just want to ask though:

If a transaction inserts a row and then is rolled back, the sequence counter isn't rolled back for performance reasons, creating a gap in primary keys.

This can cause compliance issues for some use cases such as accounting.

This risk isn't well known. Since most transactions succeed, values look sequential. Gaps will only be revealed by audits. 

Is this only applies to PostgreSQL or it applies to Django with any DB backend in general?

Thanks!

Syntax Error sequences/__init__.py, line 29 on Python 3.5.9

  File "/usr/local/lib/python3.5/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "/usr/local/lib/python3.5/site-packages/sequences/__init__.py", line 29
    ):
    ^
SyntaxError: invalid syntax

Detecting duplicate sequence name

Is there an established way to ensure a "new" sequence isn't created with the same sequence name? I'm mainly thinking about the case of a large code base with a lot of contributors.

Calling get_next_value() in multiple paralel requests can cause huge database lags

I am using django-sequences with django-plans to determine invoice sequence numbers for recurring payments.
Recurring payments renewal runs every night and currently we are processing about PayU 20 payments every night.
PayU sends payment confirmation request to the server, in which the get_next_value() is called. The requests for those payments are coming all in one or two minutes.
Sometimes those requests hit 30 second timeout, with most of the time spend in Postgres sequences_sequence insert.

I expect, that the problem might be caused the fact, that the whole Django request runs in a transaction and although I have the get_next_value() call in with transaction.atomic(): block, that it still blocks the whole sequence update until whole request is processed.

I will have to investigate this issue much deeper.

Here are some logs from the time of the long requests:


2022-02-08T01:31:37+00:00 app postgres.2123 - - [AQUA] [45-1] sql_error_code = 00000 LOG: process 2123 acquired ShareLock on transaction 226740799 after 2701.729 ms
2022-02-08T01:31:37+00:00 app postgres.2117 - - [AQUA] [32-1] sql_error_code = 00000 LOG: process 2117 acquired ShareLock on transaction 226740799 after 2701.506 ms
2022-02-08T01:31:37+00:00 app postgres.2117 - - [AQUA] [32-2] sql_error_code = 00000 CONTEXT: while inserting index tuple (1,57) in relation "sequences_sequence"
2022-02-08T01:31:37+00:00 app postgres.2117 - - [AQUA] [32-3] sql_error_code = 00000 STATEMENT:
2022-02-08T01:31:37+00:00 app postgres.2117 - - [AQUA] [32-4] INSERT INTO "sequences_sequence" (name, last)
2022-02-08T01:31:37+00:00 app postgres.2117 - - [AQUA] [32-5] VALUES ('invoice_numbers_1_call_2022_2', 448)
2022-02-08T01:31:37+00:00 app postgres.2117 - - [AQUA] [32-6] ON CONFLICT (name)
2022-02-08T01:31:37+00:00 app postgres.2117 - - [AQUA] [32-7] DO UPDATE SET last = "sequences_sequence".last + 1
2022-02-08T01:31:37+00:00 app postgres.2117 - - [AQUA] [32-8] RETURNING last;
2022-02-08T01:31:37+00:00 app postgres.2117 - - [AQUA] [32-9]
2022-02-08T01:31:37+00:00 app postgres.2116 - - [AQUA] [35-1] sql_error_code = 00000 LOG: process 2116 acquired ShareLock on transaction 226740799 after 2701.447 ms
2022-02-08T01:31:37+00:00 app postgres.2116 - - [AQUA] [35-2] sql_error_code = 00000 CONTEXT: while inserting index tuple (1,57) in relation "sequences_sequence"
2022-02-08T01:31:37+00:00 app postgres.2116 - - [AQUA] [35-3] sql_error_code = 00000 STATEMENT:
2022-02-08T01:31:37+00:00 app postgres.2116 - - [AQUA] [35-4] INSERT INTO "sequences_sequence" (name, last)
2022-02-08T01:31:37+00:00 app postgres.2116 - - [AQUA] [35-5] VALUES ('invoice_numbers_1_call_2022_2', 450)
2022-02-08T01:31:37+00:00 app postgres.2116 - - [AQUA] [35-6] ON CONFLICT (name)
2022-02-08T01:31:37+00:00 app postgres.2116 - - [AQUA] [35-7] DO UPDATE SET last = "sequences_sequence".last + 1
2022-02-08T01:31:37+00:00 app postgres.2116 - - [AQUA] [35-8] RETURNING last;
2022-02-08T01:31:37+00:00 app postgres.2116 - - [AQUA] [35-9]
2022-02-08T01:31:37+00:00 app postgres.2122 - - [AQUA] [42-1] sql_error_code = 00000 LOG: process 2122 acquired ShareLock on transaction 226740799 after 2701.444 ms
2022-02-08T01:31:37+00:00 app postgres.2122 - - [AQUA] [42-2] sql_error_code = 00000 CONTEXT: while inserting index tuple (1,57) in relation "sequences_sequence"
2022-02-08T01:31:37+00:00 app postgres.2122 - - [AQUA] [42-3] sql_error_code = 00000 STATEMENT:
2022-02-08T01:31:37+00:00 app postgres.2122 - - [AQUA] [42-4] INSERT INTO "sequences_sequence" (name, last)
2022-02-08T01:31:37+00:00 app postgres.2122 - - [AQUA] [42-5] VALUES ('invoice_numbers_1_call_2022_2', 450)
2022-02-08T01:31:37+00:00 app postgres.2122 - - [AQUA] [42-6] ON CONFLICT (name)
2022-02-08T01:31:37+00:00 app postgres.2122 - - [AQUA] [42-7] DO UPDATE SET last = "sequences_sequence".last + 1
2022-02-08T01:31:37+00:00 app postgres.2122 - - [AQUA] [42-8] RETURNING last;

and also:

2022-02-08T01:31:38+00:00 app postgres.2228 - - [AQUA] [29-1] sql_error_code = 00000 LOG: process 2228 still waiting for ShareLock on transaction 226740808 after 1001.400 ms
2022-02-08T01:31:38+00:00 app postgres.2235 - - [AQUA] [24-1] sql_error_code = 00000 LOG: process 2235 still waiting for ShareLock on transaction 226740808 after 1000.967 ms
2022-02-08T01:31:38+00:00 app postgres.2234 - - [AQUA] [23-1] sql_error_code = 00000 LOG: process 2234 still waiting for ShareLock on transaction 226740808 after 1001.173 ms
2022-02-08T01:31:38+00:00 app postgres.2236 - - [AQUA] [21-2] sql_error_code = 00000 DETAIL: Process holding the lock: 2116. Wait queue: 2197, 2236, 2228, 2117, 2070, 2122, 2239, 2071, 2123, 2234, 2235.
2022-02-08T01:31:38+00:00 app postgres.2122 - - [AQUA] [43-2] sql_error_code = 00000 DETAIL: Process holding the lock: 2116. Wait queue: 2197, 2236, 2228, 2117, 2070, 2122, 2239, 2071, 2123, 2234, 2235.
2022-02-08T01:31:38+00:00 app postgres.2236 - - [AQUA] [21-3] sql_error_code = 00000 CONTEXT: while inserting index tuple (1,58) in relation "sequences_sequence"
2022-02-08T01:31:38+00:00 app postgres.2234 - - [AQUA] [23-2] sql_error_code = 00000 DETAIL: Process holding the lock: 2116. Wait queue: 2197, 2236, 2228, 2117, 2070, 2122, 2239, 2071, 2123, 2234, 2235.
2022-02-08T01:31:38+00:00 app postgres.2228 - - [AQUA] [29-3] sql_error_code = 00000 CONTEXT: while locking tuple (1,58) in relation "sequences_sequence"
2022-02-08T01:31:38+00:00 app postgres.2122 - - [AQUA] [43-3] sql_error_code = 00000 CONTEXT: while inserting index tuple (1,58) in relation "sequences_sequence"
2022-02-08T01:31:38+00:00 app postgres.2236 - - [AQUA] [21-4] sql_error_code = 00000 STATEMENT:
2022-02-08T01:31:38+00:00 app postgres.2234 - - [AQUA] [23-3] sql_error_code = 00000 CONTEXT: while inserting index tuple (1,58) in relation "sequences_sequence"
2022-02-08T01:31:38+00:00 app postgres.2071 - - [AQUA] [50-2] sql_error_code = 00000 DETAIL: Process holding the lock: 2116. Wait queue: 2197, 2236, 2228, 2117, 2070, 2122, 2239, 2071, 2123, 2234, 2235.
2022-02-08T01:31:38+00:00 app postgres.2117 - - [AQUA] [33-1] sql_error_code = 00000 LOG: process 2117 still waiting for ShareLock on transaction 226740808 after 1000.443 ms

Feature request: Get current value

This is a great package and thank you for providing it. However, I have one feature request: what about a function to get the current value of a sequence? Sometimes I want to see where the sequence is at (in my case so I can grab the maximum). Thank you for your consideration.

django-sequences version 2.4 is not supporting

django-sequences version 2.4 showing error in installed apps.
get_next_value() giving error
relation "sequences_sequence" does not exist
LINE 2: INSERT INTO sequences_sequence (name, last)

Expand get_next_value for multiple sequences

Hi, we are using django-sequences to generate gapless sequence numbers for objects. In some requests we'll create multiple objects in the same sequence, which will all get a sequence value. Currently, we have to call get_next_value() multiple times, as the increment is hard-coded in the upsert query. This leads to an effective N+1 problem, which can harm performance in case of many sequenced objects.

I'd suggest expanding on get_next_value() with a get_next_values() that allows you to retrieve multiple sequences to distribute in a single query. Happy to create a PR for this as well.

Python 2.x support

It is shame that Python 2 support has been dropped. Is there any particular reason for that?

Unique sequence with Model.objects.bulk_create([...])

Hey @aaugustin @rmoch - I'm working on a project that needs to support some unfortunate functionality in a backwards compatible way in one of our tables. I'm using PostgreSQL 9.5.2, Django 1.9, and Python 2.7.11.

The model in need of the sequencing looks like this:

class Profile(models.Model):
    uid = models.AutoField(primary_key=True)
    unique_archaic_code = models.CharField(max_length=12L, unique=True)

We cannot change the structure of the Profile model - any performance enhancements we make on it must be backwards compatible. Otherwise I would say to heck with the unique_archaic_code system we have set up and make something more modern.

unique_archaic_code follows a specific alpha-numeric pattern. I generate values for it in the Python layer at the moment, but I want to abstract this out into a PostgreSQL SEQUENCE for obvious performance reasons, so we can potentially use a Django bulk_create statement that will make bulk creation of Profile objects with proper unique_archaic_codes possible.

unique_archaic_code must stay, and must continue to be filled with values that follow the old pattern defined in the Python layer - I want to replace that value filling with the sequencing your package appears to support.

Does this package support that functionality currently?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.