fix: v1.9.12 — gate parallel_relay fan-out to idempotent methods only (#743)

Reported in #743: with `parallel_relay > 1`, a single POST (e.g. submitting a comment) reached the destination as N concurrent requests, so the comment got posted twice. Root cause is unfixable from the Rust side: `select_ok` cancels only OUR futures, but Apps Script has no way to learn the cancellation, so every fan-out call still runs to completion and each `UrlFetchApp.fetch()` still hits the destination.

Fan-out now only triggers for idempotent methods (GET / HEAD / OPTIONS); POST / PUT / PATCH / DELETE always go sequential. Same pattern as `SAFE_REPLAY_METHODS` in Code.gs `_doBatch` fallback — safe methods are idempotent so re-firing is at worst wasteful, unsafe methods can have side effects so re-firing is incorrect.

New regression test locks down `is_method_safe_for_fanout` predicate. Tests: 180 lib + 35 tunnel-node green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
therealaleph
2026-05-05 14:16:08 +03:00
parent c2a33a80c7
commit 9a21bc44cf
4 changed files with 72 additions and 3 deletions
+4
View File
@@ -0,0 +1,4 @@
<!-- see docs/changelog/v1.1.0.md for the file format: Persian, then `---`, then English. -->
• Fix `parallel_relay` causing duplicate POSTs ([#743](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/743)): وقتی `parallel_relay > 1` تنظیم بود، تک‌POST کاربر (مثل ارسال کامنت در GitHub) به‌عنوان دو/چند درخواست همزمان به سایت مقصد می‌رسید + کامنت دو بار ثبت می‌شد. علت: `select_ok` فقط future‌های Rust سمت ما را cancel می‌کنه، ولی Apps Script سرور‌سایده هیچ خبری از cancel ندارد — هر فراخوانی fan-out روی Apps Script کامل می‌شه و `UrlFetchApp.fetch()` هر کدام به مقصد می‌رسه. حالا fan-out فقط برای متدهای **idempotent** (GET / HEAD / OPTIONS) اجرا می‌شه؛ POST / PUT / PATCH / DELETE همیشه sequential می‌رن — کاربر روی browse کاهش tail latency رو نگه می‌داره و روی form submit از duplicate side-effect ایمن می‌مونه. الگوی همان `SAFE_REPLAY_METHODS` که در `Code.gs` `_doBatch` fallback داریم. تست regression جدید locks down predicate. **۱۸۰ lib + ۳۵ tunnel-node test** همه pass.
---
• Fix `parallel_relay` causing duplicate POSTs ([#743](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/743)): with `parallel_relay > 1` set, a single user POST (e.g. submitting a comment on GitHub) was reaching the destination as two or more concurrent requests, so the comment got posted twice. Root cause: `select_ok` only cancels the loser futures on our side, but Apps Script has no way to learn about that cancellation server-side, so every fan-out call still runs to completion and each `UrlFetchApp.fetch()` to the destination still fires. Fan-out now only triggers for **idempotent** methods (GET / HEAD / OPTIONS); POST / PUT / PATCH / DELETE always go sequential — users keep the p95 tail-latency win on browsing without losing correctness on form submits. Same pattern as the `SAFE_REPLAY_METHODS` guard in `Code.gs` `_doBatch` fallback. New regression test locks down the predicate. **180 lib + 35 tunnel-node tests passing.**