Download the PHP package laith343/laravel-fcm-blast without Composer
On this page you can find all versions of the php package laith343/laravel-fcm-blast. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download laith343/laravel-fcm-blast
More information about laith343/laravel-fcm-blast
Files in laith343/laravel-fcm-blast
Package laravel-fcm-blast
Short Description High-throughput Firebase Cloud Messaging blaster for Laravel. Pushes 10k+ req/s via a reused curl_multi handle pool, OAuth handled by kreait/firebase-php.
License MIT
Homepage https://github.com/LAITH343/laravel-fcm-blast
Informations about the package laravel-fcm-blast
laravel-fcm-blast
laith343/laravel-fcm-blast
High-throughput Firebase Cloud Messaging sender for Laravel. Pushes 10k+ req/s from PHP by driving a reused curl_multi handle pool with persistent TCP — OAuth handled by kreait/firebase-php, delivery handled by the engine.
This package is backend only. It exposes a programmatic API and a status DTO; build whatever UI/monitoring you want on top of status().
Contents
- Requirements
- Supported versions
- Install
- Configure
- Environment variables
- Sizing for your target RPS
- Integrate
- Per-request debug logging
- Run a blast
- Run locally
- Run in production
- Monitor
- How it hits 10k RPS
Requirements
| Requirement | Version | Why |
|---|---|---|
| PHP | ^8.3 |
Engine + typed contracts. |
Laravel (illuminate/*) |
^12.0 | ^13.0 |
Queue, Eloquent, container, facades. |
ext-curl |
* | curl_multi is the delivery engine. |
ext-json |
* | Payload encoding. |
kreait/firebase-php |
^7.0 |
Service-account OAuth token minting. |
| Redis | — | Queue connection; required for the stale-job purge on start. |
| Database | Postgres / MySQL | Holds the per-run counters. The engine flushes atomic col = col + delta updates concurrently from every worker. |
SQLite caveat: fine for the test suite and single-worker dev runs, but its single-writer lock serializes the concurrent counter flushes — do not use it for multi-worker production blasts. Use Postgres or MySQL there.
Supported versions
| Package | PHP | Laravel |
|---|---|---|
0.x |
8.3+ | 12.x, 13.x |
Install
Configure
config/fcm-blast.php:
Real FCM needs HTTP/2. Over HTTP/1.1 every in-flight request needs its own TLS connection, so high concurrency to
fcm.googleapis.comexhausts sockets (timeouts, broken pipes).http_version => '2tls'(the default) multiplexes thousands of requests over a handful of connections — setmax_host_connectionsto something small (8-32).2tlsautomatically uses HTTP/1.1 for plain-httpendpoints, so a local mock is unaffected and can keep its many-connections setup (max_host_connections => null).
Environment variables
Every config key has an env override:
| Env var | Default | What it does |
|---|---|---|
FCM_BLAST_CREDENTIALS |
— | Service-account JSON path or string. Empty = fake-token mode (mock testing). |
FCM_BLAST_PROJECT_ID |
— | Firebase project id; builds the FCM v1 endpoint. |
FCM_BLAST_ENDPOINT |
— | Override endpoint. Empty = real FCM. Set to a mock URL for load testing. |
FCM_BLAST_RATE_CAP |
10000 |
Global sends/sec cap, split across workers (10000 = 600k/min). |
FCM_BLAST_RATE_BURST |
5% of rate | Max instantaneous burst per worker (paced limiter). Lower = smoother; doesn't change sustained rate. |
FCM_BLAST_MAX_HOST_CONNECTIONS |
16 |
TCP connections per worker. Null = rateCap*0.3 (HTTP/1.1 mock). |
FCM_BLAST_MAX_CONCURRENT_STREAMS |
100 |
Max concurrent HTTP/2 streams per connection (FCM drops >~100). |
FCM_BLAST_HTTP_VERSION |
2tls |
2tls (HTTP/2 over TLS, 1.1 over cleartext), 2, or 1.1. |
FCM_BLAST_MAX_RETRIES |
5 |
Attempts per token for 429/503 + transient transport errors. |
FCM_BLAST_QUEUE |
fcm-blast |
Queue name the jobs run on. |
FCM_BLAST_CONNECTION |
redis |
Queue connection (must be redis for stale-job purging). |
max_host_connectionsis per worker. Total connections to FCM =workers × max_host_connections. In-flight capacity per worker =max_host_connections × max_concurrent_streams(the engine sizes its concurrency to exactly this).
Sizing for your target RPS
Throughput is governed by Little's Law:
To hit a target RPS, make capacity ≥ in-flight needed, and keep FCM_BLAST_RATE_CAP at the rate you want (it caps emission so you never exceed it).
Worked example — 10,000 RPS (600k/min):
| avg latency | in-flight needed | example config (capacity) |
|---|---|---|
| 300 ms | 3,000 | 4 workers × 8 × 100 = 3,200 |
| 600 ms | 6,000 | 4 workers × 16 × 100 = 6,400 |
| 1,000 ms | 10,000 | 4 workers × 25 × 100 = 10,000 |
with 4 workers → 6,400 in-flight, enough for 10k RPS up to ~640ms latency; rate_cap then holds the ceiling at 10k.
Three real limits, in order of what you'll hit first:
- Network egress — a single host (especially residential/cellular) tolerates only so many simultaneous connections. Failures show as transport errors (timeouts/resets), now auto-retried. Keep
max_host_connectionsto what your link handles; scale out across hosts for more. - Latency — higher latency needs more in-flight for the same RPS. Measure
avg_latency_msfrom a run and plug it into the formula. - FCM project quota — above it, FCM returns
429/503(thethrottledcounter). Check/raise it in Google Cloud Console → IAM & Admin → Quotas → "Firebase Cloud Messaging API".
Don't over-provision connections. Use the smallest
connections × streamsthat covers your latency — extra connections only increase the handshake burst (more transport failures) without raising throughput, sincerate_capis the real ceiling.
Integrate
Implement two contracts. The package never touches your database directly.
Per-token context (localization, custom logic)
To tailor the message per user, have your TokenSource yield an OutboundToken
(token + arbitrary context) instead of a plain string. The context reaches
build() unchanged. It's fully optional — yielding a string keeps $context null,
so existing sources/builders are unaffected.
context can be any value (array, DTO, model) — it's passed in-process, not serialized.
Per-run context (which campaign am I serving?)
Per-token context covers the message; per-run context tells the resolved
TokenSource / MessageBuilder / InvalidTokenHandler instances which run
they're serving. Workers resolve those classes from the container with no
arguments, so without this they can't know the campaign. Implement
ContextAware and pass the payload via withContext():
The context travels through the queued job, so it must be serializable
(arrays/scalars, or SerializesModels-friendly values) — unlike per-token
context, which stays in-process. Opt-in: classes that don't implement
ContextAware are untouched.
Optional — prune permanently invalid tokens (FCM 404/400):
Per-request debug logging
Enable to capture every request's full body, context, outcome, HTTP code and latency for later debugging:
Output is one NDJSON record per request, in a daily-rotated file
(fcm-blast-requests-YYYY-MM-DD.log):
Performance: records are buffered in memory and appended in bulk on the engine's existing 0.5s flush tick (with a hard buffer cap), so the send loop does one file append per half-second per worker — not one write per request. This is far cheaper than a per-request log queue (which would dispatch more jobs than the sends themselves), so logging stays in the worker. Keep it off in production unless actively debugging.
Run a blast
start() returns the run id. Spin up one queue worker process per worker —
the process count must match the workers arg, since each job streams its
own non-overlapping slice of the tokens.
Run locally
For development you don't need real Firebase credentials. Two modes:
1. Fake-token mode — leave credentials empty. The token provider returns a
dummy bearer instead of minting a real OAuth token, so you can exercise the full
dispatch/queue/counter pipeline without Firebase:
2. Mock endpoint — point the sender at a local HTTP server to load-test the
engine without hitting FCM. Over plain http the 2tls setting auto-falls back
to HTTP/1.1, so let the pool grow:
Start a worker and kick off a blast (one worker shown):
For multiple local workers, start that many queue:work processes:
Linux / macOS (bash/zsh)
Windows (PowerShell)
Run the test suite:
Run in production
Run the workers under a process supervisor instead of by hand, and make the
process count match the workers you pass to start().
Supervisor (Linux)
/etc/supervisor/conf.d/fcm-blast.conf — numprocs=4 must match the workers you pass to start():
macOS (Homebrew supervisor)
Use the same [program:fcm-blast] block as above, adjusting command to your artisan path and user to your account.
Windows (production)
PowerShell Start-Process is fine for ad-hoc runs. For a managed service, wrap each queue:work in NSSM or run the workers inside WSL/Docker under Supervisor as above.
Monitor
throttledvstransportRetriestells you which ceiling you're hitting:throttledclimbing = FCM project quota (request more);transportRetriesclimbing = network/connection limits (fewer connections, scale hosts). Both are retried automatically and only becomefailedoncemax_retriesis exhausted.
A Laith343\FcmBlast\Events\FcmBlastCompleted event fires when a run finishes — listen for it to trigger follow-up work.
How it hits 10k RPS
- N parallel queue workers, each
floor(rate_cap_per_sec / workers)RPS. - Reused curl handle pool + persistent TCP — no
curl_closeper request, avoids ephemeral-port exhaustion. kreaitOAuth token cached in your cache store, refreshed ~10 min before expiry under a lock (no per-request token fetch, no stampede).- Paced token-bucket rate limiter per worker — refills continuously so sends are spread evenly across each second instead of bursting at the window edge. The burst is bounded by
rate_burst(not by connection count), which keeps you under FCM's sub-second quota even with a large connection pool. - Atomic delta counters flushed every 500 ms (
UPDATE ... SET col = col + delta) — use Postgres/MySQL for concurrent worker writes. - Exponential backoff + in-process retry for
429/503and transient transport errors (timeouts, connection resets, send/recv failures), so network blips self-heal; 404/400 pruning via your handler.
Ephemeral port range (one-time host tuning)
At thousands of RPS the OS can run out of ephemeral ports for outbound connections. Widen the range once:
Windows (admin PowerShell)
Linux
macOS
The reused handle pool keeps sockets alive across requests, so this mainly matters during connection ramp-up and on hosts under other network load.
All versions of laravel-fcm-blast with dependencies
ext-curl Version *
ext-json Version *
illuminate/contracts Version ^12.0|^13.0
illuminate/support Version ^12.0|^13.0
illuminate/database Version ^12.0|^13.0
illuminate/queue Version ^12.0|^13.0
illuminate/console Version ^12.0|^13.0
kreait/firebase-php Version ^7.0