← All posts
Note #gcp#pubsub#messaging#reliability#node

Cloud queue subscriptions quietly expire after 31 idle days

Google Cloud Pub/Sub deletes a subscription after 31 days without subscriber activity — that is the default, not an opt-in. A remote agent that was switched off for a month came back to a topic with no subscription and crashed on every start. Create per-client subscriptions with an empty expiration policy, and have the server re-ensure the topic and subscription whenever a client starts, so recovery never needs a human with a CLI.

The failure looked like a client bug: an agent logged Resource not found (resource=<its subscription>) and exited on every start. Its topic still existed. Its subscription didn’t.

The cause is a default. When you call createSubscription() with no options, Pub/Sub gives the subscription an expiration policy of 31 days: if nothing pulls from it for that long, Google deletes it. For a backend service that never matters. For a fleet of remote agents that can be powered off, unplugged or parked for a month, it means every long-offline client silently loses its inbox. In our project, only one per-client subscription had survived.

Three changes made it self-healing instead of a support ticket:

  1. Create subscriptions that never expire. Pass { expirationPolicy: {} }. An empty policy means “never”. For subscriptions that already exist, call setMetadata({ expirationPolicy: {} }) when they still carry a ttl.
  2. Ensure on every client start, server-side. The client already asks the server for its credentials before it subscribes. That request is the right hook: check the topic and subscription, create whatever is missing, then return the credentials. Restarting the client, a reboot, or a button in the UI all become the repair path.
  3. Make the ensure step safe to run concurrently. Two starts a few seconds apart raced, and the second metadata update failed with ABORTED: The request raced with another user request. The first had already done the job. Treat that error as success (re-read the metadata) instead of logging a failure.

The wider lesson: for anything created once per client and expected to live as long as the client, check the platform’s default lifecycle. “Idle” means something very different for a device than for a service.