0
0
Fork 0
mirror of https://github.com/healthchecks/healthchecks.git synced 2025-01-11 19:38:11 +00:00

Update Webhook transport to close db connection before network IO

Webhook requests can take 20+ seconds. During that time we hold
on to a database connection. With this commit, the Webhook transport
closes its DB connection before making a curl call.

With psycopg2 this does not have much effect. But with
psycopg 3 & connection pooling we will be able to use more
sendalerts workers than we have database connections. While one
worker is busy making a slow curl call, another worker can
grab its freed up connection and do some work.

Django's test runner is not happy with connections closed
mid-test, so I patched out close_old_connections() in affected tests.
This commit is contained in:
Pēteris Caune 2024-08-31 19:18:17 +03:00
parent 9803d77a1d
commit a463daa775
No known key found for this signature in database
GPG key ID: E28D7679E9A9EDE2
3 changed files with 16 additions and 11 deletions

View file

@ -14,6 +14,7 @@ from hc.lib.curl import CurlError
from hc.test import BaseTestCase
@patch("hc.api.transports.close_old_connections", Mock())
class NotifyWebhookTestCase(BaseTestCase):
def _setup_data(
self, value: str, status: str = "down", email_verified: bool = True

View file

@ -13,6 +13,7 @@ from typing import TYPE_CHECKING, Any, NoReturn, cast
from urllib.parse import quote, urlencode, urljoin
from django.conf import settings
from django.db import close_old_connections
from django.template.loader import render_to_string
from django.utils.html import escape
from pydantic import BaseModel, ValidationError
@ -387,24 +388,26 @@ class Webhook(HttpTransport):
if not spec.url:
raise TransportError("Empty webhook URL")
method = spec.method.lower()
url = self.prepare(spec.url, flip, urlencode=True)
headers = {}
for key, value in spec.headers.items():
# Header values should contain ASCII and latin-1 only
headers[key] = self.prepare(value, flip, latin1=True)
body, body_bytes = spec.body, None
if body and spec.method in ("POST", "PUT"):
body = self.prepare(body, flip, allow_ping_body=True)
body_bytes = body.encode()
retry = True
if notification.owner is None:
# This is a test notification.
# When sending a test notification, don't retry on failures.
retry = False
method = spec.method.lower()
body, body_bytes = spec.body, None
if body and spec.method in ("POST", "PUT"):
body = self.prepare(body, flip, allow_ping_body=True)
body_bytes = body.encode()
headers = {}
for key, value in spec.headers.items():
# Header values should contain ASCII and latin-1 only
headers[key] = self.prepare(value, flip, latin1=True)
# Give up database connection before potentially long network IO:
close_old_connections()
self.request(method, url, retry=retry, data=body_bytes, headers=headers)

View file

@ -10,6 +10,7 @@ from hc.api.models import Channel, Notification
from hc.test import BaseTestCase
@patch("hc.api.transports.close_old_connections", Mock())
class SendTestNotificationTestCase(BaseTestCase):
def setUp(self) -> None:
super().setUp()