|
7 | 7 | from core.utils.common import load_func |
8 | 8 | from django.conf import settings |
9 | 9 | from django.db.models import Q |
| 10 | +from django.db.models.query import QuerySet |
10 | 11 |
|
11 | 12 | from .models import Webhook, WebhookAction |
12 | 13 |
|
| 14 | +logger = logging.getLogger(__name__) |
| 15 | + |
13 | 16 |
|
14 | 17 | def get_active_webhooks(organization, project, action): |
15 | 18 | """Return all active webhooks for organization or project by action. |
@@ -71,37 +74,78 @@ def emit_webhooks_sync(organization, project, action, payload): |
71 | 74 | run_webhook_sync(wh, action, payload) |
72 | 75 |
|
73 | 76 |
|
74 | | -def emit_webhooks_for_instance_sync(organization, project, action, instance=None): |
75 | | - """Run all active webhooks for the action using instances as payload. |
| 77 | +def _process_webhook_batch(webhooks, project, action, batch, action_meta): |
| 78 | + """Process a single batch of instances for webhooks. |
76 | 79 |
|
77 | | - Be sure WebhookAction.ACTIONS contains all required fields. |
| 80 | + Args: |
| 81 | + webhooks: Active webhooks to send |
| 82 | + project: Project instance (optional) |
| 83 | + action: Action name |
| 84 | + batch: Batch of instances to process |
| 85 | + action_meta: Action metadata from WebhookAction.ACTIONS |
78 | 86 | """ |
79 | | - webhooks = get_active_webhooks(organization, project, action) |
80 | | - if not webhooks.exists(): |
81 | | - return |
82 | 87 | payload = {} |
83 | | - # if instances and there is a webhook that sends payload |
84 | | - # get serialized payload |
85 | | - action_meta = WebhookAction.ACTIONS[action] |
86 | 88 |
|
87 | | - if instance and isinstance(instance, list) and isinstance(instance[0], int): |
88 | | - instance = action_meta['model'].objects.filter(id__in=instance) |
89 | | - |
90 | | - if instance and webhooks.filter(send_payload=True).exists(): |
| 89 | + if batch and webhooks.filter(send_payload=True).exists(): |
91 | 90 | serializer_class = action_meta.get('serializer') |
92 | 91 | if serializer_class: |
93 | | - payload[action_meta['key']] = serializer_class(instance=instance, many=action_meta['many']).data |
| 92 | + payload[action_meta['key']] = serializer_class(instance=batch, many=action_meta['many']).data |
94 | 93 | if project and payload: |
95 | 94 | payload['project'] = load_func(settings.WEBHOOK_SERIALIZERS['project'])(instance=project).data |
96 | 95 | if payload and 'nested-fields' in action_meta: |
97 | 96 | for key, value in action_meta['nested-fields'].items(): |
98 | 97 | payload[key] = value['serializer']( |
99 | | - instance=get_nested_field(instance, value['field']), many=value['many'] |
| 98 | + instance=get_nested_field(batch, value['field']), many=value['many'] |
100 | 99 | ).data |
| 100 | + |
101 | 101 | for wh in webhooks: |
102 | 102 | run_webhook_sync(wh, action, payload) |
103 | 103 |
|
104 | 104 |
|
| 105 | +def emit_webhooks_for_instance_sync(organization, project, action, instance=None): |
| 106 | + """Run all active webhooks for the action using instances as payload. |
| 107 | +
|
| 108 | + Be sure WebhookAction.ACTIONS contains all required fields. |
| 109 | + """ |
| 110 | + webhooks = get_active_webhooks(organization, project, action) |
| 111 | + if not webhooks.exists(): |
| 112 | + return |
| 113 | + |
| 114 | + action_meta = WebhookAction.ACTIONS[action] |
| 115 | + |
| 116 | + # Convert list of IDs to queryset |
| 117 | + if instance and isinstance(instance, list) and isinstance(instance[0], int): |
| 118 | + instance = action_meta['model'].objects.filter(id__in=instance) |
| 119 | + |
| 120 | + # Check if batching is needed |
| 121 | + is_batch_collection = isinstance(instance, (list, QuerySet)) |
| 122 | + use_batching = is_batch_collection and flag_set('fflag_fix_back_plt_843_webhook_memory_improvement_12082025_short') |
| 123 | + |
| 124 | + if use_batching: |
| 125 | + # Process in batches |
| 126 | + batch_size = settings.WEBHOOK_BATCH_SIZE |
| 127 | + |
| 128 | + if isinstance(instance, QuerySet): |
| 129 | + # For QuerySets, use iterator with chunk_size |
| 130 | + total_count = instance.count() |
| 131 | + logger.debug(f'Processing webhook for {total_count} instances in batches of {batch_size}') |
| 132 | + for i in range(0, total_count, batch_size): |
| 133 | + batch = instance[i : i + batch_size] |
| 134 | + logger.debug(f'Processing batch {i // batch_size + 1} with {batch.count()} instances') |
| 135 | + _process_webhook_batch(webhooks, project, action, batch, action_meta) |
| 136 | + else: |
| 137 | + # For lists, slice directly |
| 138 | + total_count = len(instance) |
| 139 | + logger.debug(f'Processing webhook for {total_count} instances in batches of {batch_size}') |
| 140 | + for i in range(0, len(instance), batch_size): |
| 141 | + batch = instance[i : i + batch_size] |
| 142 | + logger.debug(f'Processing batch {i // batch_size + 1} with {len(batch)} instances') |
| 143 | + _process_webhook_batch(webhooks, project, action, batch, action_meta) |
| 144 | + else: |
| 145 | + # Original behavior - process all at once |
| 146 | + _process_webhook_batch(webhooks, project, action, instance, action_meta) |
| 147 | + |
| 148 | + |
105 | 149 | def run_webhook(webhook, action, payload=None): |
106 | 150 | """Run one webhook for action. |
107 | 151 |
|
|
0 commit comments