1

A Firebase client can add a task to a queue like so:

var tasks = db.ref('quote-request-queue/tasks');
tasks.push({'need': 'quote for customer'});

This task represents a request to get a price quote for an item.

On the backend, a Firebase-queue worker will pick up the task and do some work, but what is the canonical way to update the client when the backend work is done?

Should we use a property on the logged-in user model, for which the client listens for changes?

The logged-in user can listen for changes on their own model and then on a change will get the quote data?

Perhaps use another model in the DB besides the logged-in user model itself?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Alexander Mills
  • 90,741
  • 139
  • 482
  • 817

2 Answers2

1

I would put a unique identifier in the task, that you can then write into the response "queue" (it's really more of a list at that point).

So:

var tasks = db.ref('quote-request-queue/tasks');
var key = tasks.push().key;
tasks.push({'need': 'quote for customer', id: key });

And then the server writes its response in:

quote-responses
    <key>

And the client waits for a response there:

var responses = db.ref('quote-responses').child(key);
responses.on('value', function(snapshot) {
    if (snapshot.exists()) {
        // TODO: handle the response in the snapshot
    }
});

Also see: Return task results using firebase-queue

Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
1

We currently do this by using the queue's features to handle the response. The user is forced by security rule to write their user ID as a property of the request and they may read only items in the queue that contain their ID.

Security Rule (relevant part for client):

"queue": {
  "tasks": {
    ".indexOn": [
      "_state"
    ],
    "$id": {
      ".read": "auth !== null && ((!data.exists()) || (data.child('user').val() === auth.uid))",
      ".write": "auth !== null && ((!data.exists() && newData.child('user').val() === auth.uid && newData.child('_state').val() === '<start state goes here>') || (data.exists() && data.child('user').val() === auth.uid && !newData.exists()))"
    }
  }
}

This allows a client to create new jobs and delete jobs, but not modify their content.

To ensure that the client can only add an entry in the start state you will need to fill in the <start state goes here> in the rule above.

Spec

{
  "error_state" : "error",
  "finished_state" : "finished",
  "in_progress_state" : "in_progress"
}

You need a non-default spec to specify a finished state, which the client may watch for to know when the request is resolved.

Other Considerations

The client cannot be relied upon to clean up tasks used like this. You should configure the server to delete the response automatically after a timeout. The user might refresh or leave the page before a response is returned for example. This may only happen less than 1% of the time but if the queue fills up with finished responses your performance will degrade.

meticoeus
  • 543
  • 4
  • 9