Skip to content

Replace per-worker hostname cache with shared Swoole Table#11346

Merged
loks0n merged 7 commits into1.8.xfrom
perf/domain-cache
Feb 17, 2026
Merged

Replace per-worker hostname cache with shared Swoole Table#11346
loks0n merged 7 commits into1.8.xfrom
perf/domain-cache

Conversation

@loks0n
Copy link
Copy Markdown
Member

@loks0n loks0n commented Feb 17, 2026

Summary

  • Replace the Config::getParam('hostnames') per-worker PHP array cache with a shared Swoole Table ($hostnames) for tracking certificate generation status
  • The old cache was local to each worker, meaning every worker independently hit the DB for the same hostnames
  • The new Swoole Table is in shared memory, so once any worker checks a hostname, all workers see the cache hit

Test plan

  • Verify certificate generation still triggers for new main domains on first request
  • Verify subsequent requests to the same hostname skip certificate logic (cache hit)
  • Verify cache is shared across workers (second worker hitting same hostname should not query DB)

🤖 Generated with Claude Code

… cert tracking

The hostname certificate cache was using Config::getParam/setParam which stores a plain PHP array local to each worker. This meant every worker independently hit the DB for the same hostnames. Replace with a Swoole Table shared across all workers via shared memory.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@loks0n loks0n changed the base branch from main to 1.8.x February 17, 2026 12:20
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 17, 2026

📝 Walkthrough

Walkthrough

Replaces the single large domains table with two Swoole tables: riskyDomains and certifiedDomains, both registered as HTTP resources via Http::setResource. Certificate-generation router actions in app/controllers/general.php now receive an injected Table $certifiedDomains and use it (keys: md5(domain)) to read/write domain hit/miss state instead of the previous local/cache-based approach. Risk checks and the periodic DOMAIN_SYNC_TIMER now operate against riskyDomains instead of the old domains table. Minor control-flow adjustments align cache checks and persistence with the two-table design.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Replace per-worker hostname cache with shared Swoole Table' directly and clearly summarizes the main change: replacing a per-worker cache mechanism with a shared Swoole Table.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The pull request description clearly explains the motivation (replacing per-worker cache with shared Swoole Table), the problem it solves (reducing duplicate DB queries across workers), and the test plan to verify the changes work correctly.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch perf/domain-cache

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/controllers/general.php`:
- Around line 1076-1088: The cache key is inconsistent: exists() checks
md5($hostname) while set() uses md5($domain->get()), so normalize and reuse the
same key; call $hostnameRaw = $request->getHostname(), create Domain $domain =
new Domain($hostnameRaw ?: ''), compute $cacheKey = md5($domain->get()) (or md5
of the normalized string you choose) and replace both
$hostnames->exists(md5($hostname)) and $hostnames->set(md5($domain->get()), ...)
with $hostnames->exists($cacheKey) and $hostnames->set($cacheKey, ...) so the
lookup and write use the exact same key (refer to Request::getHostname,
Domain::get/isKnown/isTest, and $hostnames->exists/$hostnames->set).

In `@app/http.php`:
- Around line 47-49: The Swoole Table ($hostnames) is created with only 100_000
rows and currently has no eviction, so calls to $hostnames->set(...) can
silently return false when the table is full; update the code paths that write
to $hostnames (calls to $hostnames->set) to check the boolean return and log an
error including the key and context when set() returns false, and either
increase the initial Table size (match the $domains table’s 1_000_000 rows or
make size configurable) or add a TTL/eviction scheme by adding a timestamp
column (e.g. reuse column('value') or add 'ts') and implement a periodic cleanup
timer that removes/stales entries before they exhaust capacity. Ensure you
reference $hostnames, ->set(), column('value'), create() and the $domains sizing
as you make the change.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Feb 17, 2026

Open in StackBlitz

npm i https://pkg.pr.new/appwrite/appwrite/@appwrite.io/console@11346

commit: b9258df

@github-actions
Copy link
Copy Markdown

github-actions bot commented Feb 17, 2026

Security Scan Results for PR

Docker Image Scan Results

Package Version Vulnerability Severity
libecpg 18.1-r0 CVE-2026-2004 HIGH
libecpg 18.1-r0 CVE-2026-2005 HIGH
libecpg 18.1-r0 CVE-2026-2006 HIGH
libecpg 18.1-r0 CVE-2026-2007 HIGH
libecpg-dev 18.1-r0 CVE-2026-2004 HIGH
libecpg-dev 18.1-r0 CVE-2026-2005 HIGH
libecpg-dev 18.1-r0 CVE-2026-2006 HIGH
libecpg-dev 18.1-r0 CVE-2026-2007 HIGH
libpq 18.1-r0 CVE-2026-2004 HIGH
libpq 18.1-r0 CVE-2026-2005 HIGH
libpq 18.1-r0 CVE-2026-2006 HIGH
libpq 18.1-r0 CVE-2026-2007 HIGH
libpq-dev 18.1-r0 CVE-2026-2004 HIGH
libpq-dev 18.1-r0 CVE-2026-2005 HIGH
libpq-dev 18.1-r0 CVE-2026-2006 HIGH
libpq-dev 18.1-r0 CVE-2026-2007 HIGH
postgresql18-dev 18.1-r0 CVE-2026-2004 HIGH
postgresql18-dev 18.1-r0 CVE-2026-2005 HIGH
postgresql18-dev 18.1-r0 CVE-2026-2006 HIGH
postgresql18-dev 18.1-r0 CVE-2026-2007 HIGH

Source Code Scan Results

🎉 No vulnerabilities found!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@app/http.php`:
- Around line 47-49: The Swoole\Table instance $hostnames has no eviction and
will silently drop inserts when full; fix by (1) handling failed inserts from
$hostnames->set(...) — check its boolean return and log/metric on false — and
(2) either increase the table size passed to new Table(...) or implement an
explicit eviction strategy (e.g., maintain a rolling key via an atomic counter
and overwrite old entries) so entries aren't silently lost; update any code that
writes to $hostnames to use the new check and eviction approach (symbols:
$hostnames, Table::TYPE_INT, $hostnames->set, create).

@github-actions
Copy link
Copy Markdown

github-actions bot commented Feb 17, 2026

✨ Benchmark results

  • Requests per second: 1,809
  • Requests with 200 status code: 325,758
  • P99 latency: 0.089637769

⚡ Benchmark Comparison

Metric This PR Latest version
RPS 1,809 978
200 325,758 176,077
P99 0.089637769 0.227575036

Register both `domains` and `hostnames` tables as Http resources so they
can be injected via the framework instead of relying on closures or globals.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@app/http.php`:
- Around line 47-52: Summary: The shared $hostnames Table is correctly created
and registered via Http::setResource and requires no code changes; keep the
current DI registration for 'domains' and 'hostnames' as-is. Leave the Table
instantiation (new Table(100_000)), column definition (column('value',
Table::TYPE_INT, 1)), and Http::setResource('hostnames', fn () => $hostnames)
unchanged; ensure the previously flagged no-eviction concern for $hostnames is
tracked/fixed elsewhere (confirm mitigation or add ticket) and run the
worker/task integration tests to validate resource access.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
app/controllers/general.php (1)

1098-1101: Consider caching non-platform domains to avoid re-validation.

When a valid (known, non-test) domain is not in $platformHostnames, we return at line 1100 without writing to the cache. This means every subsequent request from this domain will re-parse and re-validate the Domain object. While it doesn't hit the DB, adding a cache entry here would skip all the validation work on repeat requests.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/controllers/general.php` around lines 1098 - 1101, When the branch
checking !$in_array($domain->get(), $platformHostnames) returns early, add a
cache entry for this non-platform domain so subsequent requests skip
re-parsing/validation; before the return, write a cache key (e.g.
"non_platform_domain:".$domain->get()) with a reasonable TTL (e.g. 1h) using the
existing caching service used elsewhere in this controller, and ensure you only
cache valid, non-test domains (the same condition that triggers the return) to
avoid caching test/invalid entries.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@app/controllers/general.php`:
- Around line 1078-1090: The cache key is inconsistent: exists(md5($hostname))
uses the raw request value while set(md5($domain->get())) uses the normalized
Domain::get(), so cache misses always occur; fix by normalizing once and using
the same key for both checks and writes (e.g., compute $normalizedHost =
$domain->get() or $domain->normalize() after creating Domain from
$request->getHostname(), then use
$certifiedDomains->exists(md5($normalizedHost)) and
$certifiedDomains->set(md5($normalizedHost), ...) and update the later write at
the location referenced (line ~1167) to also use md5($normalizedHost)).

In `@app/http.php`:
- Around line 43-52: The Swoole Table sizing/eviction issue: increase the tables
from 100_000 to a larger capacity (e.g., 1_000_000) when constructing
riskyDomains and certifiedDomains, and add handling around every Table::set(...)
call that writes into these tables to check the boolean return value and log a
warning/error (include the table name and key being inserted) when set() returns
false so we detect a full table; optionally also add instrumentation
(counter/gauge) or a periodic cleanup/TLL-like strategy if appropriate.
Reference the Table instances riskyDomains and certifiedDomains and the places
where you call Table::set(...) to add the return-value check and logging.

---

Nitpick comments:
In `@app/controllers/general.php`:
- Around line 1098-1101: When the branch checking !$in_array($domain->get(),
$platformHostnames) returns early, add a cache entry for this non-platform
domain so subsequent requests skip re-parsing/validation; before the return,
write a cache key (e.g. "non_platform_domain:".$domain->get()) with a reasonable
TTL (e.g. 1h) using the existing caching service used elsewhere in this
controller, and ensure you only cache valid, non-test domains (the same
condition that triggers the return) to avoid caching test/invalid entries.

@loks0n loks0n enabled auto-merge February 17, 2026 17:33
@loks0n loks0n disabled auto-merge February 17, 2026 17:33
@loks0n loks0n merged commit 93b542d into 1.8.x Feb 17, 2026
63 of 65 checks passed
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