Skip to content

Commit 4f0df35

Browse files
authored
[fix] Avoided re-populating VPN peers cache in post_save
Previously, ``VpnClient.post_save`` re-populated the peer cache immediately. With a large number of peers, this could make uWSGI requests slow or even timeout. Now, the method only invalidates the peer cache. The cache will be re-populated later by the ``trigger_vpn_server_endpoint`` task. Additionally, the ``trigger_vpn_server_endpoint`` task now has a higher soft time limit (300s) to accommodate generating large configurations for many peers, which could exceed the default `OpenwispCeleryTask.soft_time_limit`.
1 parent 3a45fd9 commit 4f0df35

File tree

4 files changed

+47
-31
lines changed

4 files changed

+47
-31
lines changed

openwisp_controller/config/base/vpn.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -904,7 +904,7 @@ def _get_common_name(self):
904904
@classmethod
905905
def post_save(cls, instance, **kwargs):
906906
def _post_save():
907-
instance.vpn._invalidate_peer_cache(update=True)
907+
instance.vpn._invalidate_peer_cache()
908908

909909
transaction.on_commit(_post_save)
910910
# ZT network member should be authorized and assigned

openwisp_controller/config/tasks.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,9 @@ def invalidate_devicegroup_cache_delete(instance_id, model_name, **kwargs):
9797
)
9898

9999

100-
@shared_task(base=OpenwispCeleryTask)
100+
# Generating large configurations can be time-consuming; hence,
101+
# a custom soft time limit is applied here.
102+
@shared_task(soft_time_limit=300)
101103
def trigger_vpn_server_endpoint(endpoint, auth_token, vpn_id):
102104
Vpn = load_model("config", "Vpn")
103105
try:

openwisp_controller/config/tests/test_vpn.py

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -728,9 +728,18 @@ def test_trigger_vpn_server_endpoint_invalid_vpn_id(self):
728728

729729

730730
class TestWireguardTransaction(BaseTestVpn, TestWireguardVpnMixin, TransactionTestCase):
731-
def test_auto_peer_configuration(self):
731+
@mock.patch(
732+
"openwisp_controller.config.tasks.requests.post",
733+
return_value=HttpResponse(status=200),
734+
)
735+
def test_auto_peer_configuration(self, *args):
732736
self.assertEqual(IpAddress.objects.count(), 0)
733-
device, vpn, template = self._create_wireguard_vpn_template()
737+
device, vpn, template = self._create_wireguard_vpn_template(
738+
vpn_options={
739+
"webhook_endpoint": "https://example.com:8080/trigger-update",
740+
"auth_token": "openwisp",
741+
}
742+
)
734743
vpnclient_qs = device.config.vpnclient_set
735744
self.assertEqual(vpnclient_qs.count(), 1)
736745
self.assertEqual(IpAddress.objects.count(), 2)
@@ -770,16 +779,12 @@ def test_auto_peer_configuration(self):
770779
Vpn,
771780
"invalidate_checksum_cache",
772781
return_value=vpn.invalidate_checksum_cache(),
773-
) as mocked_invalidate_checksum_cache, mock.patch.object(
774-
Vpn,
775-
"get_cached_configuration",
776-
return_value=vpn.get_cached_configuration(),
777-
) as mocked_cached_configuration:
782+
) as mocked_invalidate_checksum_cache:
778783
device2.config.templates.add(template)
779784
# The Vpn configuration cache is invalidated and re-populated
780785
mocked_invalidate_checksum_cache.assert_called_once()
781-
mocked_cached_configuration.assert_called_once()
782-
# cache is invalidated and updated, hence no queries expected
786+
# cache is invalidated and updated (by trigger_vpn_server_endpoint task),
787+
# hence no queries expected
783788
with self.assertNumQueries(0):
784789
vpn_config = vpn.get_config()["wireguard"][0]
785790
self.assertEqual(len(vpn_config.get("peers", [])), 2)
@@ -789,17 +794,12 @@ def test_auto_peer_configuration(self):
789794
Vpn,
790795
"invalidate_checksum_cache",
791796
return_value=vpn.invalidate_checksum_cache(),
792-
) as mocked_invalidate_checksum_cache, mock.patch.object(
793-
Vpn,
794-
"get_cached_configuration",
795-
return_value=vpn.get_cached_configuration(),
796-
) as mocked_cached_configuration:
797+
) as mocked_invalidate_checksum_cache:
797798
device2.delete(check_deactivated=False)
798799
mocked_invalidate_checksum_cache.assert_called_once()
799-
mocked_cached_configuration.assert_not_called()
800-
# cache is invalidated but not updated
801-
# hence we expect queries to be generated
802-
with self.assertNumQueries(1):
800+
# cache is invalidated and is updated by the
801+
# trigger_vpn_server_endpoint task
802+
with self.assertNumQueries(0):
803803
vpn_config = vpn.get_config()["wireguard"][0]
804804
self.assertEqual(len(vpn_config.get("peers", [])), 1)
805805

@@ -985,9 +985,18 @@ def test_auto_client(self):
985985
class TestVxlanTransaction(
986986
BaseTestVpn, TestVxlanWireguardVpnMixin, TransactionTestCase
987987
):
988-
def test_auto_peer_configuration(self):
988+
@mock.patch(
989+
"openwisp_controller.config.tasks.requests.post",
990+
return_value=HttpResponse(status=200),
991+
)
992+
def test_auto_peer_configuration(self, *args):
989993
self.assertEqual(IpAddress.objects.count(), 0)
990-
device, vpn, template = self._create_vxlan_vpn_template()
994+
device, vpn, template = self._create_vxlan_vpn_template(
995+
vpn_options={
996+
"webhook_endpoint": "https://example.com:8080/trigger-update",
997+
"auth_token": "openwisp",
998+
}
999+
)
9911000
vpnclient_qs = device.config.vpnclient_set
9921001
self.assertEqual(vpnclient_qs.count(), 1)
9931002
self.assertEqual(IpAddress.objects.count(), 2)
@@ -1014,17 +1023,17 @@ def test_auto_peer_configuration(self):
10141023
}
10151024
)
10161025
device2.config.templates.add(template)
1017-
# cache is invalidated and updated, hence no queries expected
1026+
# cache is invalidated and updated (by trigger_vpn_server_endpoint task),
1027+
# hence no queries expected
10181028
with self.assertNumQueries(0):
10191029
config = vpn.get_config()
10201030
peers = json.loads(config["files"][0]["contents"])
10211031
self.assertEqual(len(peers), 2)
10221032

10231033
with self.subTest("cache updated when a new peer is deleted"):
10241034
device2.delete(check_deactivated=False)
1025-
# cache is invalidated but not updated
1026-
# hence we expect queries to be generated
1027-
with self.assertNumQueries(2):
1035+
# cache is invalidated and updated (by trigger_vpn_server_endpoint task)
1036+
with self.assertNumQueries(0):
10281037
config = vpn.get_config()
10291038
peers = json.loads(config["files"][0]["contents"])
10301039
self.assertEqual(len(peers), 1)

openwisp_controller/config/tests/utils.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,11 @@ def _create_wireguard_vpn(self, config=None, **kwargs):
164164
vpn.save()
165165
return vpn
166166

167-
def _create_wireguard_vpn_template(self, auto_cert=True, **kwargs):
168-
vpn = self._create_wireguard_vpn()
167+
def _create_wireguard_vpn_template(
168+
self, auto_cert=True, vpn_options=None, **kwargs
169+
):
170+
vpn_options = vpn_options or {}
171+
vpn = self._create_wireguard_vpn(**vpn_options)
169172
org1 = kwargs.get("organization", vpn.organization)
170173
template = self._create_template(
171174
name="wireguard",
@@ -180,7 +183,7 @@ def _create_wireguard_vpn_template(self, auto_cert=True, **kwargs):
180183

181184

182185
class TestVxlanWireguardVpnMixin(CreateIpamModelsMixin):
183-
def _create_vxlan_tunnel(self, config=None):
186+
def _create_vxlan_tunnel(self, config=None, **kwargs):
184187
if config is None:
185188
config = {"wireguard": [{"name": "wg0", "port": 51820}]}
186189
org = self._get_org()
@@ -193,11 +196,13 @@ def _create_vxlan_tunnel(self, config=None):
193196
config=config,
194197
subnet=subnet,
195198
ca=None,
199+
**kwargs,
196200
)
197201
return tunnel, subnet
198202

199-
def _create_vxlan_vpn_template(self):
200-
vpn, subnet = self._create_vxlan_tunnel()
203+
def _create_vxlan_vpn_template(self, vpn_options=None):
204+
vpn_options = vpn_options or {}
205+
vpn, subnet = self._create_vxlan_tunnel(**vpn_options)
201206
org1 = vpn.organization
202207
template = self._create_template(
203208
name="vxlan-wireguard",

0 commit comments

Comments
 (0)