Autoload — The Primary Problem
None of the update_option() or add_option() calls ever pass false as the autoload argument. WordPress defaults to autoload = 'yes', which means all ~17 plugin
options are loaded on every single page request as part of WordPress's initial options bootstrap query — even on pages where the plugin does nothing with them.
That's roughly 17 extra rows in the autoload payload that every visitor triggers, regardless of whether the plugin runs.
---
Per-Request Read Pattern (Non-Admin Users)
Every non-admin, non-whitelisted page request fires clickcease_server_validation() via the send_headers hook. That function reads:
clickcease_whitelist (constructor, line 39)
clickcease_api_key (line 103)
clickcease_domain_key (line 105)
clickcease_bot_zapping_authenticated (line 106)
clickcease_secret_key (line 107)
cheq_invalid_secret (line 108)
monitoring (line 109)
cc_version_updated (line 147)
8 get_option() calls per page load. WordPress's in-memory object cache means most of these hit memory after the first one, but only if the autoloaded options
were already cached in that request — which they are. So this part is largely benign in practice, but it's unnecessary work since all these values were already
bulk-loaded.
---
The Write Problem — Multiple update_option() Calls Per Request
update_option() always writes to the database — it bypasses the object cache. The cheq_invalid_secret option can be written 3 times in a single request through
the RTI auth flow:
// rtiService.php:152 or 155 — written once during cookie check
update_option('cheq_invalid_secret', true/false);
// rtiService.php:224 or 226 — written again during hash validation
update_option('cheq_invalid_secret', true/false);
// rtiService.php:232 or 237 — written a third time on output length check
update_option('cheq_invalid_secret', true/false);
Each is a separate UPDATE wp_options SET option_value = ... WHERE option_name = 'cheq_invalid_secret' — 3 round trips to the DB that could be 1, or ideally 0
(compute the final value once, write once).
---
Timestamps Stored as Regular Options
The rate-limiting timestamps (cc-monitoring-date, cc_white_list_send_date, cc-active-domain, cc_send_plugin_latest_version) are stored as plain options —
autoloaded by default. WordPress transients exist specifically for this pattern and would be more appropriate: they carry TTL semantics natively, they can be
stored in an external object cache (Memcached/Redis) instead of the DB, and they self-clean. Using raw options for time-gating is reinventing a worse version of
transients.
---
Redundant Reads of the Same Keys
clickcease_bot_zapping_authenticated is read at lines 39, 74, 106, 183, 216, 354, 368 — 7 times across a single request's potential call stack. The object cache
means these don't all hit the DB, but it signals the plugin has no local variable caching strategy. A single read into a class property at construction time
would serve all downstream calls.
Similarly, clickcease_api_key and clickcease_domain_key are each read in clickcease_server_validation(), then read again separately inside
update_monitoring_status() (lines 341–342) even though they were already retrieved earlier in the same request.
---
Summary of Impact
┌─────────────────────────────────────────────┬─────────────────────────────────────────────────────────────┐
│ Issue │ Impact │
├─────────────────────────────────────────────┼─────────────────────────────────────────────────────────────┤
│ All 17 options autoloaded │ Adds to the autoload query on every page request, site-wide │
├─────────────────────────────────────────────┼─────────────────────────────────────────────────────────────┤
│ No autoload = false on any option │ Can't opt out without code change │
├─────────────────────────────────────────────┼─────────────────────────────────────────────────────────────┤
│ cheq_invalid_secret written 3x per request │ 2 unnecessary DB writes per RTI auth call │
├─────────────────────────────────────────────┼─────────────────────────────────────────────────────────────┤
│ Timestamps as options instead of transients │ Can't be offloaded to object cache; always hit DB │
├─────────────────────────────────────────────┼─────────────────────────────────────────────────────────────┤
│ Redundant get_option() calls │ Benign with object cache, but noisy and fragile │
└─────────────────────────────────────────────┴─────────────────────────────────────────────────────────────┘
On a low-traffic site this is invisible. On a high-traffic site with many concurrent visitors, the repeated writes and the autoload bloat compound — especially
since the send_headers hook fires early, before any output buffering, meaning these DB interactions happen synchronously on the critical path for every page
load.