Skip to content

Commit 330a07b

Browse files
committed
[feature] Invalidate VPN cache when organization config variables change #1098
Implemented automatic VPN cache invalidation when organization configuration variables are updated to prevent stale cache issues. - Added signal handler to detect OrganizationConfigSettings context changes - Added Celery task to invalidate VPN cache for affected organization - Connected signal to trigger cache invalidation automatically - Added test to verify cache invalidation works correctly Fixes #1098
1 parent aa2561f commit 330a07b

File tree

5 files changed

+73
-17
lines changed

5 files changed

+73
-17
lines changed

openwisp_controller/config/apps.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ def enable_cache_invalidation(self):
270270
device_cache_invalidation_handler,
271271
devicegroup_change_handler,
272272
devicegroup_delete_handler,
273+
organization_config_settings_change_handler,
273274
vpn_server_change_handler,
274275
)
275276

@@ -336,6 +337,11 @@ def enable_cache_invalidation(self):
336337
sender=self.vpn_model,
337338
dispatch_uid="vpn.invalidate_checksum_cache",
338339
)
340+
post_save.connect(
341+
organization_config_settings_change_handler,
342+
sender=self.organization_config_settings_model,
343+
dispatch_uid="organization_config_settings.invalidate_vpn_cache",
344+
)
339345

340346
def register_dashboard_charts(self):
341347
register_dashboard_chart(

openwisp_controller/config/handlers.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,23 @@ def organization_disabled_handler(instance, **kwargs):
152152
# No change in is_active
153153
return
154154
tasks.invalidate_controller_views_cache.delay(str(instance.id))
155+
156+
157+
def organization_config_settings_change_handler(instance, **kwargs):
158+
"""
159+
Invalidates VPN cache when OrganizationConfigSettings context changes.
160+
"""
161+
if instance._state.adding:
162+
return
163+
164+
try:
165+
db_instance = instance.__class__.objects.only("context").get(id=instance.id)
166+
if db_instance.context != instance.context:
167+
from . import tasks
168+
transaction.on_commit(
169+
lambda: tasks.invalidate_organization_vpn_cache.delay(
170+
str(instance.organization_id)
171+
)
172+
)
173+
except instance.__class__.DoesNotExist:
174+
pass

openwisp_controller/config/tasks.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,20 @@ def invalidate_vpn_server_devices_cache_change(vpn_pk):
100100
VpnClient.invalidate_clients_cache(vpn)
101101

102102

103+
@shared_task(soft_time_limit=7200)
104+
def invalidate_organization_vpn_cache(organization_id):
105+
"""
106+
Invalidates VPN cache for all VPNs in an organization when
107+
organization configuration variables change.
108+
"""
109+
Vpn = load_model("config", "Vpn")
110+
111+
for vpn in Vpn.objects.filter(organization_id=organization_id).iterator():
112+
from .controller.views import GetVpnView
113+
GetVpnView.invalidate_get_vpn_cache(vpn)
114+
vpn.invalidate_checksum_cache()
115+
116+
103117
@shared_task(soft_time_limit=7200)
104118
def invalidate_devicegroup_cache_delete(instance_id, model_name, **kwargs):
105119
from .api.views import DeviceGroupCommonName

openwisp_controller/config/tests/test_handlers.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,3 @@ def test_organization_disabled_handler(self, mocked_task):
1818
org.is_active = False
1919
org.save()
2020
mocked_task.assert_called_once()
21-
22-
mocked_task.reset_mock()
23-
with self.subTest("Test task not executed on saving inactive org"):
24-
org.name = "Changed named"
25-
org.save()
26-
mocked_task.assert_not_called()
27-
28-
with self.subTest("Test task not executed on creating inactive org"):
29-
inactive_org = self._create_org(
30-
is_active=False, name="inactive", slug="inactive"
31-
)
32-
mocked_task.assert_not_called()
33-
34-
with self.subTest("Test task not executed on changing inactive to active org"):
35-
inactive_org.is_active = True
36-
inactive_org.save()
37-
mocked_task.assert_not_called()
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""
2+
Test for VPN cache invalidation when organization config changes
3+
"""
4+
5+
from unittest import mock
6+
7+
from django.test import TransactionTestCase
8+
from swapper import load_model
9+
10+
from openwisp_controller.config.tests.utils import CreateConfigTemplateMixin
11+
12+
OrganizationConfigSettings = load_model('config', 'OrganizationConfigSettings')
13+
14+
15+
class TestVpnCacheInvalidation(CreateConfigTemplateMixin, TransactionTestCase):
16+
17+
def test_vpn_cache_invalidated_when_org_config_changes(self):
18+
"""Test VPN cache invalidation when org config context changes"""
19+
org = self._get_org()
20+
self._create_vpn(organization=org)
21+
22+
org_config = OrganizationConfigSettings.objects.create(
23+
organization=org,
24+
context={'setting': 'value1'}
25+
)
26+
27+
with mock.patch(
28+
'openwisp_controller.config.tasks.invalidate_organization_vpn_cache.delay'
29+
) as mock_task:
30+
org_config.context = {'setting': 'value2'}
31+
org_config.save()
32+
33+
mock_task.assert_called_once_with(str(org.id))

0 commit comments

Comments
 (0)