Skip to content

[mod_event_socket] Look up myevents headers once per event instead of once per listener - #3233

Draft
disolaterX wants to merge 1 commit into
signalwire:masterfrom
disolaterX:mod_event_socket-myevents-header-lookup
Draft

disolaterX wants to merge 1 commit into
signalwire:masterfrom
disolaterX:mod_event_socket-myevents-header-lookup

Conversation

@disolaterX

Copy link
Copy Markdown

Problem

event_handler() in mod_event_socket runs every event past every listener while holding globals.listener_mutex. For each listener with LFLAG_MYEVENTS, it calls:

  • switch_event_get_header(event, "unique-id")
  • switch_event_get_header_nil(event, "Job-Owner-UUID")

Each call ends in switch_event_get_header_ptr(), which walks the event's header list linearly. Channel events often carry more than 100 headers. The result is the same for every listener, but it is computed again for each one. Job-Owner-UUID is only added to BACKGROUND_JOB events from bgapi (mod_event_socket.c, api_exec). On channel events the lookup scans the whole list and finds nothing. With N myevents listeners, each event costs about 2N header-list scans.

This is the common setup for outbound ESL: one socket per call, each sending myevents.

Measurements (reporter's)

These come from a production system on a88d069 and a local repro. I have not measured on current master.

  • Production saturated the event dispatch thread at about 1,300 concurrent calls, each with its own outbound socket using myevents.
  • A perf sample of the dispatch thread showed switch_event_get_header_ptr at 48% and event_handler at 14%.
  • A local repro with 1,300 per-call myevents sockets drove the thread to 95-104% CPU and delivered 6,930 events 1 s or more late.

Fix

Look up both headers once per event, before taking the listener mutex. The loop uses the cached values.

Why behavior is unchanged

  • uuid is still the raw switch_event_get_header() result, so it can be NULL. The !uuid check is unchanged.
  • job_owner_uuid is still the switch_event_get_header_nil() result, so it is "" when the header is absent. The strcmp() sees the same value as before.
  • Nothing in the loop modifies event. Listeners get a switch_event_dup() clone. The header pointers stay valid for the whole call.
  • The order of the two checks and the send assignments are unchanged.

One tradeoff: each event now pays two header lookups even when no listener uses myevents. Before, those lookups only ran for myevents listeners that passed the earlier checks. Two scans per event is small next to the 2N it replaces.

Out of scope

The loop still visits every listener for every event under the global mutex, and the filter-header loop still runs per listener. Indexing myevents listeners by session UUID would remove the per-listener walk. That is a larger change, and I'd rather discuss it separately.

Testing

  • clang -fsyntax-only -Wall -Wextra -Wshadow -Wdeclaration-after-statement on mod_event_socket.c against an unconfigured tree: no errors, and no warnings in event_handler(). Warnings were identical before and after the change. (macOS, Apple clang 21. switch_am_config.h was stubbed and the SWITCH_*_T_FMT macros were defined by hand, because the tree was not configured.)
  • I did not do a full build or a runtime test on master. I did not run tests/unit/test_mod_event_socket.c. Its tests cover content-length handling and sendevent, not myevents filtering.

… once per listener

event_handler() fetched the unique-id and Job-Owner-UUID headers inside the
listener loop for every myevents listener. Each lookup walks the event's
header list, so an event cost about two list scans per myevents listener while
globals.listener_mutex was held. Fetch both headers once before the loop.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant