DEV Community

Cover image for My music app no longer needs my computer on
Andy Yeung
Andy Yeung

Posted on Originally published at amsyn.org

My music app no longer needs my computer on

I have a small music app that I built for myself. It's a PWA that plays the tracks I've collected, with the audio stored in the cloud and a simple library on top. Playing music was never the hard part. Adding a track was. Each new track needs a slow job: fetch it, convert it, upload it, then add it to the library.

The old setup: the app called my computer

That slow job ran on my home computer. A small API server sat on it, exposed to the internet through a tunnel, and the app called it directly whenever I added a track.

It worked, but it had some real problems:

  • The computer had to be on. If it was off, adding a track just failed, and I had to try again once I was home.
  • There was a server open to the internet. It was a server on my home network that I had to keep locked down.
  • The progress bar was fake. The app only knew the request had been sent. The rest was an animation.
  • Errors disappeared. When an upload credential expired, uploads failed quietly for a while. The only sign was a line in a console nobody was watching.

The new setup: the database is the queue

The fix came down to one change: the app no longer calls the worker. It writes a job into a table, and the worker comes to collect it.

  • The worker only makes outgoing connections, to the database and to storage. No tunnel, and nothing open to the internet.
  • If the computer is off, jobs simply wait in the table. When it's back on, the worker picks them up one by one.
  • As it works, the worker writes its progress back into the job, and the app shows it live. The progress bar is real now.
  • The worker also checks in regularly, so the app can show whether the worker is online.

Postgres already has what you need to hand out jobs safely: a worker can claim the next waiting job in a way that no other worker can claim it at the same time. If a worker dies halfway through, the job is released after a while and tried again, a few times at most, before it's marked as failed with a Retry button in the app. No Redis or separate queue service, and the free tier of my database covers it.

A queue is something people can write to

Moving the work into a table brings up a new question: who is allowed to write to it? Whatever lands in the table, the worker will do. So:

  • Only approved accounts can add jobs, and only for themselves.
  • Every job is validated twice: by the database when it's inserted, and again by the worker before it runs. Only the kind of link the app expects is accepted. Without that, a job could ask the worker to fetch an address on my home network.
  • A job can only touch the person's own library, even though the worker itself has broader permissions.

What changed

  • My computer can be off. It used to mean "the app is broken". Now it just means "new tracks show up a bit later".
  • Nothing is open to the internet. The worker only reaches out.
  • Progress and errors are real. If something fails, the reason shows up in the app, with a Retry button.
  • No new services. The database I already had does the job.

Using a database as a queue isn't a new idea. For a small personal project that you only use now and then, though, it's just enough: it turned "my computer must always be on" from a hard requirement into something I don't have to think about.

Cover: Old vinyl records lined shelf by Unknown, CC0 1.0.

Top comments (0)