Skip to content

Commit cd89431

Browse files
committed
Use context vars instead of thread-local storage for change logging
1 parent 8400509 commit cd89431

File tree

5 files changed

+31
-35
lines changed

5 files changed

+31
-35
lines changed

netbox/extras/context_managers.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
from django.db.models.signals import m2m_changed, pre_delete, post_save
44

55
from extras.signals import clear_webhooks, clear_webhook_queue, handle_changed_object, handle_deleted_object
6-
from netbox import thread_locals
7-
from netbox.request_context import set_request
6+
from netbox.context import current_request, webhooks_queue
87
from .webhooks import flush_webhooks
98

109

@@ -16,8 +15,8 @@ def change_logging(request):
1615
1716
:param request: WSGIRequest object with a unique `id` set
1817
"""
19-
set_request(request)
20-
thread_locals.webhook_queue = []
18+
current_request.set(request)
19+
webhooks_queue.set([])
2120

2221
# Connect our receivers to the post_save and post_delete signals.
2322
post_save.connect(handle_changed_object, dispatch_uid='handle_changed_object')
@@ -35,8 +34,8 @@ def change_logging(request):
3534
clear_webhooks.disconnect(clear_webhook_queue, dispatch_uid='clear_webhook_queue')
3635

3736
# Flush queued webhooks to RQ
38-
flush_webhooks(thread_locals.webhook_queue)
39-
del thread_locals.webhook_queue
37+
flush_webhooks(webhooks_queue.get())
4038

41-
# Clear the request from thread-local storage
42-
set_request(None)
39+
# Clear context vars
40+
current_request.set(None)
41+
webhooks_queue.set([])

netbox/extras/signals.py

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@
77
from django_prometheus.models import model_deletes, model_inserts, model_updates
88

99
from extras.validators import CustomValidator
10-
from netbox import thread_locals
1110
from netbox.config import get_config
12-
from netbox.request_context import get_request
11+
from netbox.context import current_request, webhooks_queue
1312
from netbox.signals import post_clean
1413
from .choices import ObjectChangeActionChoices
1514
from .models import ConfigRevision, CustomField, ObjectChange
@@ -30,7 +29,7 @@ def handle_changed_object(sender, instance, **kwargs):
3029
if not hasattr(instance, 'to_objectchange'):
3130
return
3231

33-
request = get_request()
32+
request = current_request.get()
3433
m2m_changed = False
3534

3635
def is_same_object(instance, webhook_data):
@@ -69,13 +68,14 @@ def is_same_object(instance, webhook_data):
6968
objectchange.save()
7069

7170
# If this is an M2M change, update the previously queued webhook (from post_save)
72-
webhook_queue = thread_locals.webhook_queue
73-
if m2m_changed and webhook_queue and is_same_object(instance, webhook_queue[-1]):
71+
queue = webhooks_queue.get()
72+
if m2m_changed and queue and is_same_object(instance, queue[-1]):
7473
instance.refresh_from_db() # Ensure that we're working with fresh M2M assignments
75-
webhook_queue[-1]['data'] = serialize_for_webhook(instance)
76-
webhook_queue[-1]['snapshots']['postchange'] = get_snapshots(instance, action)['postchange']
74+
queue[-1]['data'] = serialize_for_webhook(instance)
75+
queue[-1]['snapshots']['postchange'] = get_snapshots(instance, action)['postchange']
7776
else:
78-
enqueue_object(webhook_queue, instance, request.user, request.id, action)
77+
enqueue_object(queue, instance, request.user, request.id, action)
78+
webhooks_queue.set(queue)
7979

8080
# Increment metric counters
8181
if action == ObjectChangeActionChoices.ACTION_CREATE:
@@ -91,7 +91,7 @@ def handle_deleted_object(sender, instance, **kwargs):
9191
if not hasattr(instance, 'to_objectchange'):
9292
return
9393

94-
request = get_request()
94+
request = current_request.get()
9595

9696
# Record an ObjectChange if applicable
9797
if hasattr(instance, 'to_objectchange'):
@@ -101,8 +101,9 @@ def handle_deleted_object(sender, instance, **kwargs):
101101
objectchange.save()
102102

103103
# Enqueue webhooks
104-
webhook_queue = thread_locals.webhook_queue
105-
enqueue_object(webhook_queue, instance, request.user, request.id, ObjectChangeActionChoices.ACTION_DELETE)
104+
queue = webhooks_queue.get()
105+
enqueue_object(queue, instance, request.user, request.id, ObjectChangeActionChoices.ACTION_DELETE)
106+
webhooks_queue.set(queue)
106107

107108
# Increment metric counters
108109
model_deletes.labels(instance._meta.model_name).inc()
@@ -113,10 +114,8 @@ def clear_webhook_queue(sender, **kwargs):
113114
Delete any queued webhooks (e.g. because of an aborted bulk transaction)
114115
"""
115116
logger = logging.getLogger('webhooks')
116-
webhook_queue = thread_locals.webhook_queue
117-
118-
logger.info(f"Clearing {len(webhook_queue)} queued webhooks ({sender})")
119-
webhook_queue.clear()
117+
logger.info(f"Clearing {len(webhooks_queue.get())} queued webhooks ({sender})")
118+
webhooks_queue.set([])
120119

121120

122121
#

netbox/netbox/__init__.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +0,0 @@
1-
import threading
2-
3-
thread_locals = threading.local()

netbox/netbox/context.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from contextvars import ContextVar
2+
3+
__all__ = (
4+
'current_request',
5+
'webhooks_queue',
6+
)
7+
8+
9+
current_request = ContextVar('current_request')
10+
webhooks_queue = ContextVar('webhooks_queue')

netbox/netbox/request_context.py

Lines changed: 0 additions & 9 deletions
This file was deleted.

0 commit comments

Comments
 (0)