Skip to content

Commit f63869e

Browse files
committed
add extra test and update README.md
1 parent 5ce8834 commit f63869e

File tree

2 files changed

+82
-0
lines changed

2 files changed

+82
-0
lines changed

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,34 @@ Extra settings explanation:
525525
Do not hardcode database name when you define replicated table or distributed table.
526526
Because test database name is different from deployed database name.
527527

528+
#### Clickhouse cluster behind a load balancer
529+
530+
If your clickhouse cluster is running behind a load balancer, you can optionally set `distributed_migrations` to `True` under database OPTIONS.
531+
Then a distributed migration table will be created on all nodes of the cluster, and all migration operations will be performed on this
532+
distributed migrations table instead of a local migrations table. Otherwise, sequentially running migrations will have no effect on other nodes.
533+
534+
Configuration example:
535+
536+
```python
537+
DATABASES = {
538+
"default": {
539+
"HOST": "clickhouse-load-balancer",
540+
"PORT": 9000,
541+
"ENGINE": "clickhouse_backend.backend",
542+
"OPTIONS": {
543+
"migration_cluster": "cluster",
544+
"distributed_migrations": True,
545+
"settings": {
546+
"mutations_sync": 2,
547+
"insert_distributed_sync": 1,
548+
"insert_quorum": 2,
549+
"alter_sync": 2,
550+
},
551+
},
552+
}
553+
}
554+
```
555+
528556
### Model
529557

530558
`cluster` in `Meta` class will make models being created on cluster.

tests/migrations/test_loader.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import os
33
from copy import deepcopy
44
from importlib import import_module
5+
from time import sleep
56

67
from django.db import connection, connections
78
from django.db.migrations.exceptions import (
@@ -99,6 +100,28 @@ def assertMigrationTablesExists(self, conn):
99100
self.assertIn("distributed_django_migrations", tables)
100101
i -= 1
101102

103+
def assertMigrationExists(self, conn, name, app, deleted=False, tries=5):
104+
# simulate multiple attempts to read a migration from the distributed migration table
105+
while tries:
106+
with conn.cursor() as cursor:
107+
cursor.execute(f"SELECT * FROM distributed_django_migrations where name = '{name}'")
108+
res = cursor.fetchall()
109+
110+
try:
111+
self.assertEqual(len(res), 1)
112+
except AssertionError:
113+
# handle replication lag
114+
if tries >= 1:
115+
sleep(1)
116+
tries -= 1
117+
continue
118+
raise ValueError(f"Migration {name} for app {app} not found in distributed_django_migrations table")
119+
120+
self.assertEqual(res[0][1], app)
121+
self.assertEqual(res[0][2], name)
122+
self.assertEqual(res[0][-1], deleted)
123+
tries -= 1
124+
102125
def test_distributed_migration_schema_with_existing_migrations(self):
103126
"""
104127
Tests that migration tables are created in all nodes even if the django_migrations table already exists
@@ -154,6 +177,37 @@ def test_distributed_migration_schema_without_migrations(self):
154177

155178
self.assertMigrationTablesExists(recorder.connection)
156179

180+
def test_apply_unapply_distributed(self):
181+
"""
182+
Tests marking migrations as applied/unapplied in a distributed setup.
183+
"""
184+
databases = [x for x in self.databases if x != "s1r2"]
185+
186+
for db in databases:
187+
conn = connections[db]
188+
recorder = MigrationRecorder(conn)
189+
recorder.flush()
190+
191+
lb = DatabaseWrapper(deepcopy(self.lb), alias="load_balancer")
192+
connections["load_balancer"] = lb
193+
recorder = MigrationRecorder(lb)
194+
195+
recorder.record_applied("myapp", "0432_ponies")
196+
197+
for db in databases:
198+
conn = connections[db]
199+
self.assertMigrationExists(conn, "0432_ponies", "myapp", deleted=False)
200+
201+
self.assertMigrationExists(connections['load_balancer'], "myapp", "0432_ponies")
202+
203+
recorder.record_unapplied("myapp", "0432_ponies")
204+
205+
for db in databases:
206+
conn = connections[db]
207+
self.assertMigrationExists(conn, "0432_ponies", "myapp", deleted=True)
208+
209+
self.assertMigrationExists(connections['load_balancer'], "myapp", "0432_ponies", deleted=True)
210+
157211

158212
class LoaderTests(TestCase):
159213
"""

0 commit comments

Comments
 (0)