Skip to content

Commit 912f18c

Browse files
committed
[change] Replace third-party JSONField with native models.JSONField and optimize indexing #673
Modernized JSON storage using native Django fields and PostgreSQL GIN indexes. Includes SeparateDatabaseAndState for safe schema migration, SpatiaLite compatibility guards, and enhanced CI health-checks. Closes #673
1 parent 5bfcaa9 commit 912f18c

5 files changed

Lines changed: 132 additions & 25 deletions

File tree

.github/workflows/ci.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,26 @@ jobs:
8989
sudo npm install -g prettier
9090
9191
- name: Start InfluxDB
92+
id: start_influxdb
9293
if: ${{ !cancelled() && steps.deps.conclusion == 'success' }}
9394
run: docker compose up -d influxdb
9495

96+
- name: Wait for InfluxDB
97+
if: ${{ !cancelled() && steps.deps.conclusion == 'success' && steps.start_influxdb.conclusion == 'success' }}
98+
run: |
99+
for i in {1..20}; do
100+
if [ "$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8086/ping)" = "204" ]; then
101+
echo "InfluxDB is ready!"
102+
exit 0
103+
fi
104+
echo "Waiting for InfluxDB to initialize..."
105+
if [ "$i" -lt 20 ]; then
106+
sleep 3
107+
fi
108+
done
109+
echo "Error: InfluxDB timed out"
110+
exit 1
111+
95112
- name: QA checks
96113
run: |
97114
./run-qa-checks

openwisp_monitoring/monitoring/migrations/0013_auto_20260224_0730.py

Lines changed: 0 additions & 25 deletions
This file was deleted.
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import django.contrib.postgres.indexes
2+
from django.db import migrations, models
3+
4+
5+
def migrate_to_jsonb(apps, schema_editor):
6+
if schema_editor.connection.vendor != "postgresql":
7+
return
8+
with schema_editor.connection.cursor() as cursor:
9+
cursor.execute(
10+
"DROP INDEX IF EXISTS monitoring_metric_main_tags_2d8550ae_like; "
11+
"DROP INDEX IF EXISTS monitoring_metric_main_tags_2d8550ae;"
12+
)
13+
cursor.execute(
14+
"ALTER TABLE monitoring_metric ALTER COLUMN main_tags TYPE jsonb USING "
15+
"CASE WHEN main_tags IS NULL OR btrim(main_tags) = '' THEN '{}'::jsonb "
16+
"ELSE main_tags::jsonb END; "
17+
"ALTER TABLE monitoring_metric ALTER COLUMN extra_tags TYPE jsonb USING "
18+
"CASE WHEN extra_tags IS NULL OR btrim(extra_tags) = '' THEN '{}'::jsonb "
19+
"ELSE extra_tags::jsonb END;"
20+
)
21+
cursor.execute(
22+
"CREATE INDEX IF NOT EXISTS main_tags_gin_idx ON monitoring_metric USING gin (main_tags); "
23+
"CREATE INDEX IF NOT EXISTS extra_tags_gin_idx ON monitoring_metric USING gin (extra_tags);"
24+
)
25+
26+
27+
def rollback_jsonb(apps, schema_editor):
28+
if schema_editor.connection.vendor != "postgresql":
29+
return
30+
with schema_editor.connection.cursor() as cursor:
31+
cursor.execute(
32+
"DROP INDEX IF EXISTS main_tags_gin_idx; DROP INDEX IF EXISTS extra_tags_gin_idx;"
33+
)
34+
cursor.execute(
35+
"ALTER TABLE monitoring_metric ALTER COLUMN main_tags TYPE text; "
36+
"ALTER TABLE monitoring_metric ALTER COLUMN extra_tags TYPE text;"
37+
)
38+
cursor.execute(
39+
"CREATE INDEX IF NOT EXISTS monitoring_metric_main_tags_2d8550ae "
40+
"ON monitoring_metric (main_tags); "
41+
"CREATE INDEX IF NOT EXISTS monitoring_metric_main_tags_2d8550ae_like "
42+
"ON monitoring_metric (main_tags text_pattern_ops);"
43+
)
44+
45+
46+
class Migration(migrations.Migration):
47+
dependencies = [
48+
("monitoring", "0012_migrate_signal_metrics"),
49+
]
50+
51+
operations = [
52+
migrations.SeparateDatabaseAndState(
53+
state_operations=[
54+
migrations.AlterField(
55+
model_name="metric",
56+
name="extra_tags",
57+
field=models.JSONField(
58+
blank=True,
59+
default=dict,
60+
verbose_name="extra tags",
61+
),
62+
),
63+
migrations.AlterField(
64+
model_name="metric",
65+
name="main_tags",
66+
field=models.JSONField(
67+
blank=True,
68+
db_index=True,
69+
default=dict,
70+
verbose_name="main tags",
71+
),
72+
),
73+
migrations.AddIndex(
74+
model_name="metric",
75+
index=django.contrib.postgres.indexes.GinIndex(
76+
fields=["main_tags"],
77+
name="main_tags_gin_idx",
78+
),
79+
),
80+
migrations.AddIndex(
81+
model_name="metric",
82+
index=django.contrib.postgres.indexes.GinIndex(
83+
fields=["extra_tags"],
84+
name="extra_tags_gin_idx",
85+
),
86+
),
87+
],
88+
database_operations=[
89+
migrations.RunPython(migrate_to_jsonb, reverse_code=rollback_jsonb),
90+
],
91+
),
92+
]

requirements-test.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ django-redis~=6.0.0
33
mock-ssh-server~=0.9.1
44
channels_redis~=4.3.0
55
freezegun~=1.5.5
6+
commitizen~=4.13.0 # required by openwisp-qa-check commit validation
7+
prompt-toolkit!=3.0.52 # avoid questionary regression (questionary#454), fixed in 2.1.1+
8+
django-filter>=24.0,<25.0 # 25.x removed deprecated DRF schema helpers used in our stack

tests/openwisp2/sample_monitoring/migrations/0004_alter_metric_field_name.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Generated by Django 3.2.15 on 2022-09-08 11:11
22

3+
import django.contrib.postgres.indexes
34
from django.db import migrations, models
45

56

@@ -19,4 +20,23 @@ class Migration(migrations.Migration):
1920
max_length=16,
2021
),
2122
),
23+
migrations.SeparateDatabaseAndState(
24+
state_operations=[
25+
migrations.AddIndex(
26+
model_name="metric",
27+
index=django.contrib.postgres.indexes.GinIndex(
28+
fields=["main_tags"],
29+
name="main_tags_gin_idx",
30+
),
31+
),
32+
migrations.AddIndex(
33+
model_name="metric",
34+
index=django.contrib.postgres.indexes.GinIndex(
35+
fields=["extra_tags"],
36+
name="extra_tags_gin_idx",
37+
),
38+
),
39+
],
40+
database_operations=[],
41+
),
2242
]

0 commit comments

Comments
 (0)