Search code examples
google-cloud-functionsdialogflow-esactions-on-googledialogflow-es-fulfillment

DialogFlow Testing Cloud Function concurrency


I have a Google Assistant action with fulfilment through Firebase Cloud Functions. I understand that Cloud Functions may share instances between invocations, and that you can use the Global scope to do heavy lifting and preparation. My function instantiates a global class that has serialised some JSON and handles returning data and other tasks in my function. I have variables in this class that are set when the function is called, and I have been careful to make sure that the variables are all set using the conv.data session data object that is unique to the current conversation. The hope is that although the class instance may exist between different invocations, and possibly by different users, it will still be contextualised to the local scope, and I wont see any variables being overwritten by other sessions.

Which brings me to the question, which is, how can I test this? I have tried to test on my mobile device using the Google Assistant app, at the same time as testing in the browser console. I witnessed the two sessions getting merged together, and it was an unholy mess, but I am not sure if that was the global scope, or just that I was testing two sessions with the same user account.

Can anyone enlighten me on whether it is possible to run two of the same action using the same user account? It looked like the conv.data object had a mix of the two different sessions I was running which suggests it was using the same conversation token for both sessions.

Another question would be, do you think using a global class to store state across invocations is going to be an issue with different users? The docs do state that only one invocation of the function can ever happen at a time. So there shouldn't be any race condition type scenarios.


Solution

  • Dialogflow should keep the data in conv.data isolated to a single session, even sessions from the same user. When you're using Dialogflow, this data is stored in a Context, which is session specific.

    You can verify this by turning StackDriver logging on, which will let you examine the exact request and response that Dialogflow is using with your fulfillment, and this will include the session ID for tracking. (And if you think it is mixing the two, posting the request and response details would help figure out what is going on.)

    Very roughly, it sounds like you're getting something mixed into your global, or possibly something set in one session that isn't cleared or overwritten by a different one. Again - seeing the exact requests and responses should help you (and/or us) figure that out.

    My attitude is that a global such as this should be treated as read-only. If you want to have some environment object that contains the relevant information for just this session - I'd keep that separate, just from a philosophical design.

    Certainly I wouldn't use this global state to store information between sessions. While a function will only be invoked, I'm not sure how that would work with Promises - which you'll need once you start any async operations. It also runs the risk that subsequent invocations might be on different instances.

    My approach, in short, (which I make pretty firm in multivocal):

    • Store all state in a Context (which conv.data should so).
    • Access this via the request, conv, or some other request-specific object that you create.
    • Global information / configuration should be read-only.