|
60 | 60 | run: | |
61 | 61 | composer self-update && composer --version |
62 | 62 | composer install --prefer-dist |
63 | | - # TEMPORARY: diagnose intermittent `cURL error 28` timeouts to ps.pndsn.com |
64 | | - # from the self-hosted runner. Remove once the runner egress issue is resolved. |
65 | | - - name: Diagnose ps.pndsn.com connectivity |
66 | | - continue-on-error: true |
67 | | - run: | |
68 | | - echo "=== Runner public egress IP ===" |
69 | | - curl -s --max-time 15 https://api.ipify.org || echo "(could not determine egress IP)" |
70 | | - echo |
71 | | - echo "=== DNS resolution for ps.pndsn.com ===" |
72 | | - getent hosts ps.pndsn.com || nslookup ps.pndsn.com || true |
73 | | - echo |
74 | | - echo "=== Proxy-related environment ===" |
75 | | - env | grep -iE 'proxy' || echo "(no proxy env vars set)" |
76 | | - echo |
77 | | - echo "=== 10x probe of https://ps.pndsn.com/time/0 (catch intermittent hang) ===" |
78 | | - for i in $(seq 1 10); do |
79 | | - echo "--- attempt $i ---" |
80 | | - curl -sS -o /dev/null --max-time 15 \ |
81 | | - -w 'http_code=%{http_code} dns=%{time_namelookup}s connect=%{time_connect}s tls=%{time_appconnect}s total=%{time_total}s\n' \ |
82 | | - "https://ps.pndsn.com/time/0" \ |
83 | | - || echo "attempt $i FAILED (curl exit $?)" |
84 | | - done |
85 | | - echo |
86 | | - echo "=== Burst probe A: 200x time/0, 20 concurrent (keyless baseline) ===" |
87 | | - # Cheap, unauthenticated endpoint. Establishes that the network path is fine |
88 | | - # under concurrent load. time/0 is NOT subject to per-key publish throttling. |
89 | | - burst_log="$(mktemp)" |
90 | | - seq 1 200 | xargs -P 20 -I {} \ |
91 | | - curl -sS -o /dev/null --max-time 15 \ |
92 | | - -w 'http_code=%{http_code} total=%{time_total}s\n' \ |
93 | | - "https://ps.pndsn.com/time/0" \ |
94 | | - >> "$burst_log" 2>&1 || true |
95 | | - echo "--- HTTP status code distribution ---" |
96 | | - grep -oE 'http_code=[0-9]+' "$burst_log" | sort | uniq -c || true |
97 | | - echo "--- slowest 5 ---" |
98 | | - sort -t= -k3 -rn "$burst_log" | head -5 || true |
99 | | - echo |
100 | | - echo "=== Burst probe B: 200x PUBLISH, 20 concurrent (the actual failing op) ===" |
101 | | - # Publishes to the real keyset — the same operation that times out in CI. |
102 | | - # If THIS bursts shows 429 / 000 / ~10s stalls while probe A stayed fast, |
103 | | - # the cause is per-key publish throttling, not the network or the runner. |
104 | | - pub_log="$(mktemp)" |
105 | | - seq 1 200 | xargs -P 20 -I {} \ |
106 | | - curl -sS -o /dev/null --max-time 15 \ |
107 | | - -w 'http_code=%{http_code} total=%{time_total}s\n' \ |
108 | | - "https://ps.pndsn.com/publish/$PUBLISH_KEY/$SUBSCRIBE_KEY/0/diag-burst/0/%22x%22?uuid=diag-burst" \ |
109 | | - >> "$pub_log" 2>&1 || true |
110 | | - echo "--- HTTP status code distribution ---" |
111 | | - grep -oE 'http_code=[0-9]+' "$pub_log" | sort | uniq -c || true |
112 | | - echo "--- non-200 / slow (>1s) responses ---" |
113 | | - grep -E 'http_code=(000|4|5)|total=[1-9]' "$pub_log" || echo "(none — all fast 200s)" |
114 | | - echo "--- slowest 5 ---" |
115 | | - sort -t= -k3 -rn "$pub_log" | head -5 || true |
116 | | - # TEMPORARY: raw `curl` CLI control that reuses ONE keep-alive connection |
117 | | - # across many publishes (one curl process, N urls via -K). This is the |
118 | | - # PHP-free reproducer for the server team: if it hangs every ~10th here on |
119 | | - # the dense CI runner, the every-10th black-hole is reproducible with no |
120 | | - # PHP/Guzzle/SDK involvement at all. Locally (high RTT) it ran 200/200 |
121 | | - # clean — this step tests it on the same runner/pool that fails for Guzzle. |
122 | | - # Remove once the timeout cause is confirmed/fixed. |
123 | | - - name: Diagnose connection reuse (raw curl CLI) |
124 | | - continue-on-error: true |
125 | | - run: | |
126 | | - curl --version | head -1 |
127 | | - DIAG_ITERATIONS=200 ./scripts/diag-curl.sh |
128 | | - # TEMPORARY: PHP-level probe that REUSES one Guzzle client across many |
129 | | - # requests, exactly like the SDK does for a whole PHPUnit run. The curl |
130 | | - # probes above open a fresh connection each time and so cannot reproduce a |
131 | | - # stale/half-closed keep-alive socket — the prime suspect for |
132 | | - # "connected fine, then 0 bytes received after 10s" (cURL error 28). |
133 | | - # DIAG_DELAY_MS inserts an idle gap to provoke LB idle-timeout of pooled |
134 | | - # sockets. Remove once the timeout cause is confirmed/fixed. |
135 | | - - name: Diagnose Guzzle connection reuse (PHP) |
136 | | - continue-on-error: true |
137 | | - run: | |
138 | | - # Reuses one Guzzle client like the SDK. The keyless time/0 baseline |
139 | | - # should be 0 failures; the publish run reproduces the cURL-28 timeout. |
140 | | - # The built-in spacing sweep then tells us whether spacing publishes |
141 | | - # further apart makes the failures disappear (== server rate limit). |
142 | | - DIAG_ITERATIONS=200 DIAG_DELAY_MS=0 php scripts/diag-guzzle.php |
143 | 63 | - name: Run unit tests |
144 | 64 | run: composer test |
145 | 65 | - name: Cancel workflow runs for commit on error |
|
0 commit comments