azure-oss/storage-queue
A Laravel/PHP package for integrating Azure Storage Queues, providing a queue driver that lets your application push and process jobs through Microsoft Azure's cloud queue service using the Azure SDK.
What this package actually is. azure-oss/storage-queue is a low-level PHP client for the Azure Storage Queue REST API, published as a read-only subtree split from the php-oss-for-azure/azure-storage-queue-php monorepo. It is not a Laravel package: there is no service provider, no queue connector, no config file, no facade. It gives us authenticated primitives (create/delete queue, send/receive/peek/update/delete message, queue metadata) and nothing about Laravel's job lifecycle. Everything Laravel-shaped — Illuminate\Contracts\Queue\Queue, a Connector, payload serialization, failed-job handling — is work we own.
Architecture fit. Superficially strong, structurally awkward. Azure Storage Queue maps to Laravel's push/pop/delete/release model through visibility timeouts, but three mismatches dominate the design:
messageId + popReceipt to delete or update a message. Laravel's job abstraction assumes you can delete by job ID. Our adapter must smuggle the pop receipt through the job payload or a side channel, and any receipt refresh must happen before the visibility timeout expires.failed_jobs table can absorb failures, but Azure itself will keep redelivering poison messages until dequeueCount exceeds a threshold we implement. This is custom code, not configuration.later() beyond a week is impossible), approximate size(), and no ordering guarantees. Also note losing Redis means losing Horizon — a real observability regression for teams standardized on it.Integration feasibility. A working driver is a 1–2 week spike: AzureQueue extends Queue, an AzureConnector, a service provider, and contract tests against Laravel's queue test suite. Production parity — retries, backoff, poison handling, metrics, graceful shutdown — is 4–6 weeks including hardening. Before building, confirm whether an existing Laravel Azure queue driver can accept this client as a pluggable backend; wrapping a maintained driver beats owning one.
Technical risk. The dominant risk is stewardship, not code. Zero stars, zero dependents, a read-only split that routes issues upstream, and no Microsoft backing mean we are effectively the maintainer of last resort. Auth appears to be connection-string/account-key oriented — verify whether managed identity / Entra ID is supported, because account keys in env vars will fail a security review in most Azure shops. Lock-in risk is manageable only if we confine the SDK behind our own interface and never call AzureOss\Storage\Queue types from application code.
Key questions.
popReceipt, dequeueCount, and approximateMessageCount cleanly, and can we refresh a receipt?Recommendation: conditional pilot, not a default. Fund a timeboxed spike on one non-critical queue. Proceed only if auth meets security review and the pop-receipt handling proves clean.
Stack fit. Composer-installable, framework-agnostic, MIT — good citizen in a Laravel app. It slots in as a dependency of our driver, never as a direct dependency of domain code. Target config/queue.php with a new azure connection, AZURE_STORAGE_* env vars, and a QUEUE_CONNECTION switch per environment.
Migration path. Additive, not invasive. Stand up the Azure connection alongside the existing Redis/database connection, route a single low-risk queue (notifications, exports, webhooks) to it, and keep the old path warm for rollback. The driver boundary is the whole point: application code calls dispatch(), so the swap is configuration plus one deploy — provided we have not leaked SDK types.
Compatibility. Three surfaces to verify ourselves, since nobody else will: Laravel version support (test 11 and 12 explicitly), PHP version constraints against our runtime, and Azure API version behavior. Azurite for local/CI is a gate — if the SDK cannot talk to the emulator, developer experience degrades and integration tests get expensive. Subtree splits sometimes lag the monorepo; pin exact versions and treat upgrades as reviewed changes, not composer update side effects.
Sequencing.
Maintenance. Two artifacts to own: the pinned SDK and our adapter. Budget ~1–2 engineer-days per quarter for upgrades plus incident support. Because the upstream is a small community project with no dependents, treat abandonment as a plausible scenario — decide now whether the fallback is forking the monorepo or reverting to Redis. Keep a vendored fork path documented.
Support. No SLA, no vendor escalation, community issues only. Internalize this: our on-call is the support tier. That is acceptable for a secondary queue and unacceptable for anything on the critical path.
Scaling. Azure Storage Queue scales by account and partition rather than by cluster, which is genuinely attractive versus operating Redis. In practice, throughput is bounded by our worker concurrency and by per-transaction costs: polling is chatty, so mandate long polling and batch receive, and watch the storage transaction bill on high-frequency queues. Cost modeling should compare transactions-per-job against the Redis instance we retire — not against zero.
Failure modes to instrument before launch.
dequeueCount threshold fires. Get the threshold and alerting right or you get silent hot loops.Retry-After under burst; the driver must honor it, not retry hot.Ramp-up. Low for developers (they keep using dispatch()); moderate for ops (new failure signatures, new dashboards, new runbook). Plan a half-day enablement plus a written runbook covering poison messages, stuck queues, and rollback to the previous connection.
Verdict. A defensible, cheap, Azure-native escape hatch from Redis — worth a bounded pilot, with the explicit understanding that we are adopting a client library and inheriting a driver. Approve the spike; defer the platform decision until the auth and pop-receipt questions are answered with evidence.
How can I help you explore Laravel packages today?