Mugsy

View Original

Backend Bits

Hey folks! Hope all is well out there. Tonight I would like to give a brief rundown on how Mugsy’s updated Decaf API works to control the brewing process.

It’s a fairly deep subject, and there is a lot of info to share. But I’m crazy beat today and already starting to crash. In the interest of getting the basics out tonight, I will keep it light (mostly), with more info to come soon.

APIs + Long Running Tasks

One of the issues of combining a REST API with a coffee machine, is that when a client sends a request to an API to do some work, it expects a near instant response from the API letting the client know how it went. This is nice and quick when the API is controlling the manipulation of data, but not so simple when you are brewing a cup of coffee. It’s only takes 3 minutes, but that might as well be 3 hours to the frontend.

Previously, when Decaf received a brew request, it would take the received recipeId , use that id to pull the recipe steps from the database, and then send all of those details to a monolithic python script as arguments. That single python script would check the machine status and then spin up the brewing hardware and proceed to control and monitor the brew process. Once one block of code completed, say for the water heater as an example, it would then start up the next function block to control the grinder. And on and on until the brew completed. There was some siloing of hardware using Decaf endpoints, and some semblance of process control, but it wasn’t amazing, that is for sure. And to top it all off, only when the brew completed did the script let the API know everything went ok and to finally send the 200 OK response back to Mugsy’s frontend. This worked, but there were some very obvious issues:

  • The process was brittle and errors were hard to diagnose.

  • Hard to test individual pieces of hardware or steps of the brew process.

  • Sending brew status details back to the frontend was kludgy.

  • Spaghetti code!

  • It didn’t really fit in with the REST spec’s expectations on how a system should respond and react.

Enter the Task Queue

Decaf has been completely rewritten using FastAPI as the API framework, MongoDB for the data store and Redis + RQ for brew task management. Here is a breakdown of what the brew control process looks like now:

  1. Mugsy’s front end sends a brew request to Decaf. The request contains a recipeId.

  2. Decaf receives the recipeId and immediately:

    1. Checks the status of the machine and pulls the recipe details.

    2. If the machine status is ready and able to brew, decaf then sends the brew command to the Brewing Task Queue.

    3. Once the command is sent to the task queue, Decaf sends the 200 OK response to the frontend and lets it know the brew process has started. From the frontend’s perspective, this response is effectively instantaneous.

  3. The task queue is now in control of the brew process and Decaf is open to continue responding to new requests. The first task does the following:

    1. Checks the machine status again.

    2. Confirms the recipe details again.

    3. If everything checks out, it sends a request to the water heater endpoint. This endpoint fires off a new task which is responsible for the water heater and only the water heater.

    4. Updates brewHistory and machineStatus through new Decaf requests.

    5. Once these steps are completed, this first task ends and that end state is communicated to the task manager (redis/rq).

  4. The water heating task is now running. It will:

    1. Check the heater status

    2. If everything looks good, it will start heating up and polling the temperature.

    3. It will continually update the machineStatus so the frontend can display the current temp and any other required info.

    4. Once the expected temp is reached, it fires off a request to the grinder endpoint which then starts its own task.

    5. The task completes and that end state is communicated to the task manager.

  5. The grinder starts grinding and the above process of task creation/completion continues until the brew process is completed. No need to keep writing it out for each step…

There are several benefits to having each brew step be it’s own task/process:

  • The brew process is easier to manage and orchestrate.

  • Safer: a failed step will immediately stop the brew process without any other monitoring or intervention.

  • Code only ever interacts with the hardware it is responsible for.

  • Tasks can be configured to be dependent on other tasks.

  • Once a task is started, its code is completely decoupled from the API.

  • All brew steps are decoupled from each other and completely independent.

  • Tasks can easily be monitored in real time through the task queue manager.

I feel like Decaf is now in a really good place and ready to go. Here are some quick notes about the current setup:

  • Multiple tasks can be enqueued in one step. I currently have each task sending new Decaf requests after completion because the granularity is helping me understand how we can best use the functionality moving forward. I have a lot more to share on this specific subject and setup, but will save that for some time in the next week or two.

  • Since the tasks are decoupled from the API while brewing, the front end continually polls the machineStatus endpoint to display relevant info. It’s smooth and works great but there are other methods to handle this, most notably, using websockets to stream the status. I think this may be the direction we will go in, but it’s probably going to be saved for some future update, as the current state of Decaf is working very well.

Other Bits

One of the awesome benefits of FastAPI is automatic Swagger/OpenAPI docs. Once an endpoint and schema is created, it is immediately available to view in Swagger:

I have also been working on tweaking the Github setup so all repos are in one place. MugsyOS is now set up as its own org in Github and everything will be much easier to access and manage moving forward.

Some notes on the new setup:

  • I still have to set up a bunch of stuff: keys, contribution rules/workflows and more. Once that is done, it will open up for anyone to start submitting PRs and playing with the code.

  • I have not pushed any code up to these repos yet and I don’t expect to start doing so for another few days. I will have more info on this in the next update.

  • You can access it now at github.com/MugsyOS. Feel free to follow the org/repos so you will be notified when things start getting pushed.

Ok, that is all for now. Next update will be in few days, probably Thursday/Friday. It will be mostly software focused with some additional info on shipping prospects. Thanks!!