0001-use-pg_advisory_lock-to-prevent-running-cronjobs-dur.patch
hobo/multitenant/locking.py | ||
---|---|---|
1 |
from django.db import connection, Error |
|
2 | ||
3 |
LOCK_ID = 9999999 |
|
4 | ||
5 | ||
6 |
def take_global_lock(wait=2): |
|
7 |
'''Prevent global actions, like cron or migration to run at the same time''' |
|
8 |
with connection.cursor() as c: |
|
9 |
if wait: |
|
10 |
c.execute("SET lock_timeout = '%ss'" % wait) |
|
11 |
try: |
|
12 |
c.execute('SELECT PG_ADVISORY_LOCK(%s)', [LOCK_ID]) |
|
13 |
except Error: |
|
14 |
return False |
|
15 |
return True |
hobo/multitenant/management/commands/migrate_schemas.py | ||
---|---|---|
4 | 4 |
if django.VERSION >= (1, 7, 0): |
5 | 5 |
from django.core.management.commands.migrate import Command as MigrateCommand |
6 | 6 |
from django.db.migrations.recorder import MigrationRecorder |
7 |
from django.core.management.base import CommandError |
|
7 | 8 |
from django.db import connection |
8 | 9 |
from django.conf import settings |
9 | 10 | |
10 | 11 |
from tenant_schemas.utils import get_public_schema_name, schema_exists |
11 | 12 |
from hobo.multitenant.middleware import TenantMiddleware, TenantNotFound |
12 | 13 |
from hobo.multitenant.management.commands import SyncCommon |
14 |
from hobo.multitenant.locking import take_global_lock |
|
13 | 15 | |
14 | 16 | |
15 | 17 |
class MigrateSchemasCommand(SyncCommon): |
... | ... | |
34 | 36 | |
35 | 37 |
def handle(self, *args, **options): |
36 | 38 |
super(MigrateSchemasCommand, self).handle(*args, **options) |
39 |
# migration job can wait for finishing cron jobs |
|
40 |
if not take_global_lock(wait=3600): |
|
41 |
raise CommandError('global lock is taken, migration or another cron job is running') |
|
37 | 42 |
self.PUBLIC_SCHEMA_NAME = get_public_schema_name() |
38 | 43 | |
39 | 44 |
if self.sync_public and not self.schema_name: |
hobo/multitenant/management/commands/tenant_command.py | ||
---|---|---|
10 | 10 | |
11 | 11 |
from hobo.multitenant.management.commands import InteractiveTenantOption |
12 | 12 |
from hobo.multitenant.middleware import TenantMiddleware |
13 |
from hobo.multitenant.locking import take_global_lock |
|
14 | ||
13 | 15 | |
14 | 16 |
class Command(InteractiveTenantOption, BaseCommand): |
15 | 17 |
help = "Wrapper around django commands for use with an individual tenant" |
... | ... | |
43 | 45 |
args_namespace, args = args_parser.parse_known_args(argv) |
44 | 46 | |
45 | 47 |
if args_namespace.all_tenants: |
48 |
if not take_global_lock(): |
|
49 |
raise CommandError('global lock is taken, migration or another cron job is running') |
|
46 | 50 |
for tenant in TenantMiddleware.get_tenants(): |
47 | 51 |
connection.set_tenant(tenant) |
48 | 52 |
klass.run_from_argv(args) |
tests_multitenant/test_tenant_command.py | ||
---|---|---|
1 | 1 |
import pytest |
2 | 2 |
import mock |
3 | 3 |
import os |
4 |
import threading |
|
4 | 5 | |
5 | 6 |
pytestmark = pytest.mark.django_db |
6 | 7 | |
... | ... | |
53 | 54 |
assert False |
54 | 55 |
all_tenants = list(TenantMiddleware.get_tenants()) |
55 | 56 |
assert len(all_tenants) == 1 |
57 | ||
58 | ||
59 |
@mock.patch('django.contrib.sessions.management.commands.clearsessions.Command.handle') |
|
60 |
def test_all_tenants_global_lock(handle, tenants): |
|
61 |
from django.core.management import execute_from_command_line |
|
62 |
from hobo.multitenant.locking import take_global_lock |
|
63 | ||
64 |
handle.side_effect = RecordTenant() |
|
65 |
assert take_global_lock() |
|
66 | ||
67 |
l = [0] |
|
68 | ||
69 |
def thread_run(): |
|
70 |
execute_from_command_line(['manage.py', 'tenant_command', 'clearsessions', '--all-tenants']) |
|
71 |
l[0] = 1 |
|
72 | ||
73 |
t = threading.Thread(target=thread_run) |
|
74 |
t.start() |
|
75 |
t.join() |
|
76 | ||
77 |
assert l[0] == 0 |
|
78 |
assert handle.call_count == 0 |
|
56 |
- |