Usage¶
A very simple example is shown below. It is broken into three parts:
definition
invocation (calling)
execution (receiving)
Note however that this is (nearly) a single codebase; though you are deploying to two places (some application and a cloud function to dispatch to) the deployable artifacts only differ in main.py
, if at all.
Definition¶
Defining a dispatchable function looks like this:
@dispatch
def my_func(a, b, cheer='hooray!'):
if a > b:
log.warning('a is too large!')
else:
log.info('a is just right: %s', cheer)
Note that my_func
is just a normal Python function that takes arguments. Arguments can be anything that pickle
knows how to serialize, which covers all primitives, dataclasses, and many other structures, but does not include generators or callables (among other things). The only thing you need to do to any Python function to cause it to dispatch is to apply the @dispatch
decorator.
Invocation¶
Once your function is defined in your application, you call it just like any other function:
def call_dispatched_function():
a = 1
b = 2
my_func(a, b)
When you execute this code by calling my_module.call_dispatched_function()
, your cloud function will be invoked and the specified function will be executed remotely.
Note: for obvious reasons, the dispatched execution won’t work until the cloud function is properly deployed as specified below.
Execution¶
Your cloud function will need an entry point. You should use the provided execute
method:
# this file is the entrypoint for dispatched functions; the only purpose is to
# provide a function (`execute_dispatched`) for cloud functions to call,
# and to ensure all the functions are imported and registered (any function
# that is not imported at startup will not be visible to
# `cloud_functions_dispatch`)
from cloud_functions_dispatch import execute as execute_dispatched # noqa
import functions # noqa
Note: the main.py
file deployed with the executor may or may not be the same as that deployed with the invoker; this is really dependent on whether your main application also deploys as a cloud function or as something else.
Your codebase can then be deployed to Cloud Functions with the entrypoint specified:
$ gcloud functions deploy ${FUNCTION_NAME} --trigger-topic=${TOPIC_NAME} --source=${SOURCE_DIR} --entry-point=execute_dispatched ...
Output¶
Once your function is deployed, you can call it and you will see something like the following in your logs:
I 2020-03-13T22:24:35.427Z my-topic 1043501608491014 pushing function call to pubsub: functions.my_func
… followed shortly by something like this in your cloud function logs, showing execution of the function:
D 2020-03-13T22:24:35.436Z my-topic 1043501608491013 Function execution started
I 2020-03-13T22:24:35.526Z my-topic 1043501608491013 received event 1043501608491013 sent at 2020-03-13T22:24:35.095Z
I 2020-03-13T22:24:35.526Z my-topic 1043501608491013 executing: functions.my_func
I 2020-03-13T22:24:35.526Z my-topic 1043501608491013 a is just right: hooray!
D 2020-03-13T22:24:35.527Z my-topic 1043501608491013 Function execution took 92 ms, finished with status: 'ok'
Deployable Example¶
A complete, working sample can be found in the ./sample
directory. To see it in action:
Enable Cloud Functions and PubSub in GCP (see Google documentation for this)
Create a pubsub topic in GCP (the command below assumes it’s
my-topic
)Pick an unused name for your function (the command below assumes it’s
my-func
)
Then run the following command:
$ gcloud functions deploy my-func --trigger-topic=my-topic --source=sample --entry-point=execute_dispatched --runtime=python37 --memory=128MB --max-instances=1 --set-env-vars CLOUD_FUNCTIONS_DISPATCH_GCP_PUBSUB_TOPIC=my-topic
Now set GCP_PROJECT and CLOUD_FUNCTIONS_DISPATCH_GCP_PUBSUB_TOPIC locally and:
$ cd sample
$ PYTHONPATH=..:$PYTHONPATH python -c "import functions; functions.echo(1, 'a', x=7); functions.my_func(1, 2)"
After a few seconds you will see the output of your cloud function showing the arguments passed in your cloud logging tools.