{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Callbacks From Transformers To Driver\n", "\n", "Transformers are the only Fugue extensions to execute on remote worker nodes. For some scenarios, the transformers need to communicate with the driver while it is running. For example, a transformer is training a Keras model, and at the end of each epoch, it needs to report the metrics to the driver and the driver can respond with a decision whether to stop the training.\n", "\n", "In Fugue, the callback model is also abstract, you only need to define the callback functions and specify the server to handle remote calls.\n", "\n", "## The simplest example\n", "\n", "The simplest way to have a call back, is to define a callback parameter in the interfaceless way. You only need to annotate the parameter with `callable`, `Callable` or `Callable` with arguments, for example `Callable[[str],str]`. And this parameter must be after all dataframe parameters and before all other parameters." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "from fugue import FugueWorkflow\n", "\n", "# schema: *\n", "def print_describe_and_return(df:pd.DataFrame, cb:callable) -> pd.DataFrame:\n", " cb(str(df.describe()))\n", " return df\n", "\n", "dag = FugueWorkflow()\n", "df = dag.df([[0,0],[1,1],[0,1],[2,2]],\"a:int,b:int\")\n", "df.partition(by=[\"a\"]).transform(print_describe_and_return, callback = lambda x:print(x)).show()\n", "\n", "dag.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the above example, it's a typical interfaceless transformer example with two additions: `cb:callback` in the transformer, and `callback = lambda x:print(x)` in the transform function. `cb:callback` is to tell Fugue that we want to use a callback inside the transformation. `callback = lambda x:print(x)` is to define the function that will execute on the driver.\n", "\n", "As you can see, since there are 3 partitions of `a`, there are 3 descriptions printed, and in the end, the output dataframe is also printed.\n", "\n", "You can make the callback optional" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from typing import Optional, Callable, Any\n", "\n", "# schema: *\n", "def print_describe_and_return(df:pd.DataFrame, cb:Optional[Callable[[Any],None]]) -> pd.DataFrame:\n", " if cb is not None:\n", " cb(str(df.describe()))\n", " return df\n", "\n", "dag = FugueWorkflow()\n", "df = dag.df([[0,0],[1,1],[0,1],[2,2]],\"a:int,b:int\")\n", "df.partition(by=[\"a\"]).transform(print_describe_and_return, callback = lambda x:print(x)).show()\n", "\n", "dag.run()\n", "\n", "dag2 = FugueWorkflow()\n", "df = dag2.df([[0,0],[1,1],[0,1],[2,2]],\"a:int,b:int\")\n", "df.partition(by=[\"a\"]).transform(print_describe_and_return).show()\n", "\n", "dag2.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the above example we use `Optional` to tell Fugue that this transformer can work with or without the callback. The transformer code is responsible to check null on the callback parameter. And if you don't provide a callback handler when you invoke the transformer, the transformer side will get None on the callback parameter.\n", "\n", "This is a more flexible way where your transformer can be used in different situations.\n", "\n", "\n", "## Callbacks on distributed execution engines\n", "\n", "The above code is running using `NativeExecutionEngine` running on the current process. It's the minimal code to test whether your callback logic will work. To run it using a distributed engine, You need to setup the callback server to handle network calls." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from fugue_spark import SparkExecutionEngine\n", "\n", "conf = {\n", " \"fugue.rpc.server\": \"fugue.rpc.flask.FlaskRPCServer\",\n", " \"fugue.rpc.flask_server.host\": \"0.0.0.0\",\n", " \"fugue.rpc.flask_server.port\": \"1234\",\n", " \"fugue.rpc.flask_server.timeout\": \"2 sec\",\n", "}\n", "\n", "dag.run(SparkExecutionEngine(conf=conf))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The above code uses the built-in flask server to handle network calls from workers. To use `fugue.rpc.flask.FlaskRPCServer`, you must set `fugue.rpc.flask_server.host` and `fugue.rpc.flask_server.port`, and it's suggested to also set `fugue.rpc.flask_server.timeout` to a reasonable timeout for your own case.\n", "\n", "You can also create your custom server by implementing [RPCServer](https://fugue.readthedocs.io/en/latest/api/fugue.rpc.html#fugue.rpc.base.RPCServer) and [RPCClient](https://fugue.readthedocs.io/en/latest/api/fugue.rpc.html#fugue.rpc.base.RPCClient). For example you may create a pair of server and client to communicate with [MLFlow](https://mlflow.org/) to update metrics in real time.\n", "\n", "\n", "## Stateful callbacks\n", "\n", "Commonly, callbacks need to be stateful. In Fugue, it's totally fine to set the callback to be a method of an instance (in order to be stateful), or to use a global method/variable. You only need to make the function thread safe because it could be invoked in parallel." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from threading import RLock\n", "\n", "class Callback(object):\n", " def __init__(self):\n", " self.n=0\n", " self._update_lock = RLock()\n", " \n", " def should_skip(self):\n", " with self._update_lock:\n", " self.n+=1\n", " return self.n>=3\n", " \n", "callback = Callback()\n", " \n", " \n", "# schema: *\n", "def take(df:pd.DataFrame, skip:callable) -> pd.DataFrame:\n", " if not skip():\n", " return df\n", "\n", "dag = FugueWorkflow()\n", "df = dag.df([[0,0],[1,1],[0,1],[2,2]],\"a:int,b:int\")\n", "df.partition(by=[\"a\"]).transform(take, callback = callback.should_skip).show()\n", "\n", "dag.run()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the above example, we only take two partitions of the entire dataframe, so the `Callback` implemented a thread safe counter, and return true or false based on the counter.\n", "\n", "**The only requirement** for a callback function that Fugue can use is that its input parameters and output are picklable (Nones are fine). The function itself is OK if not picklable. In the above case, `should_skip` invoked `RLock` which is not picklable, but it doesn't matter.\n", "\n", "\n", "## Implementing `RPCHandler` instead\n", "\n", "In most case the above native approaches are sufficient. However, if you want to having more control on the callback side, you can directly implement [RPCHandler](https://fugue.readthedocs.io/en/latest/api/fugue.rpc.html#fugue.rpc.base.RPCHandler). For example, you want to start a thread to process the incoming calls and stop the thread when the execution finishes." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from threading import RLock\n", "from fugue.rpc import RPCHandler\n", "from uuid import uuid4\n", "\n", "class Callback(RPCHandler):\n", " def __init__(self):\n", " super().__init__() # <-- must call\n", " self.n=0\n", " self._update_lock = RLock()\n", " \n", " def __uuid__(self) -> str:\n", " \"\"\"UUID that can affect the determinism of the workflow\"\"\"\n", " return str(uuid4()) # in this case, it will make the workflow non deterministic\n", "\n", " def start_handler(self) -> None:\n", " \"\"\"User implementation of starting the handler\"\"\"\n", " print(\"counter started\")\n", "\n", "\n", " def stop_handler(self) -> None:\n", " \"\"\"User implementation of stopping the handler\"\"\"\n", " print(\"counter stopped\")\n", " \n", " def __call__(self):\n", " with self._update_lock:\n", " self.n+=1\n", " return self.n>=3\n", " \n", "callback = Callback()\n", " \n", " \n", "# schema: *\n", "def take(df:pd.DataFrame, skip:callable) -> pd.DataFrame:\n", " if not skip():\n", " return df\n", "\n", "dag = FugueWorkflow()\n", "df = dag.df([[0,0],[1,1],[0,1],[2,2]],\"a:int,b:int\")\n", "df.partition(by=[\"a\"]).transform(take, callback = callback).show()\n", "\n", "print(dag.spec_uuid()) # every time, the id will be different because the Callback is not deterministic\n", "dag.run()\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Using callbacks in Transformer class\n", "\n", "If you must implement a `Transformer`, `OutputTransformer`, `CoTransformer` and `OutputCoTransformer`, then you can use `callback` property as the callback." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from fugue import FugueWorkflow, Transformer\n", "\n", "class PrintAndReturn(Transformer):\n", " def get_output_schema(self, df):\n", " return df.schema\n", " \n", " def transform(self, df):\n", " self.callback(str(df.as_pandas().describe()))\n", " return df\n", "\n", "dag = FugueWorkflow()\n", "df = dag.df([[0,0],[1,1],[0,1],[2,2]],\"a:int,b:int\")\n", "df.partition(by=[\"a\"]).transform(PrintAndReturn, callback = lambda x:print(x)).show()\n", "\n", "dag.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## A real example: ploting mins in real time" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "from fugue import FugueWorkflow\n", "from fugue.rpc import RPCHandler\n", "import pandas as pd\n", "import random\n", "from IPython.display import clear_output\n", "from threading import RLock, Thread\n", "from time import sleep\n", "import matplotlib.pyplot as plt\n", "\n", "class PlotMinNow(RPCHandler):\n", " def __init__(self):\n", " super().__init__()\n", " self._update_lock = RLock()\n", " self._values = []\n", " self._updated = False\n", " self._shutdown = False\n", " self._thread = None\n", " \n", " def __call__(self, value):\n", " with self._update_lock:\n", " if len(self._values)==0 or value0:\n", " clear_output()\n", " pd.Series(data).plot()\n", " plt.show()\n", " self._updated=False\n", "\n", " while not self._shutdown:\n", " _plot()\n", " sleep(1)\n", " _plot()\n", " \n", " self._thread = Thread(target=thread)\n", " self._thread.start()\n", " \n", " \n", " def stop_handler(self):\n", " self._shutdown=True\n", " self._thread.join()\n", " \n", "with PlotMinNow().start() as p:\n", " p(10)\n", " p(9.5)\n", " p(8)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "def create() -> pd.DataFrame:\n", " return pd.DataFrame([[100-x] for x in range(100)], columns=[\"a\"])\n", "\n", "def plot(df:pd.DataFrame, p:callable) -> None:\n", " random.seed(0)\n", " for v in df[\"a\"]/100.0:\n", " p(random.random()*v)\n", " sleep(0.2)\n", "\n", "with FugueWorkflow() as dag:\n", " dag.create(create).out_transform(plot, callback=PlotMinNow())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Use with caution\n", "\n", "Callbacks may be convenient to use, but you should use with caution. For example, it may not be a good idea to direct worker sider logs to driver using this approach because the amount of data can be unexpectedly large.\n", "\n", "Also, when you implement the driver side logic for the callbacks, you should be careful about the contention and latency. Take a look at the `_plot` in `PlotMinNow`, it's a consumer competing with the data producers (remote callers), so it minimizes the logic inside the locked part to reduce such contention.\n", "\n", "The CPU usage is also a concern, when multiple workers are calling back, it could overload the system. So you should consider decoupling producers and consumers and moving the expensive operations to the consumer side so that you have better control of the load. See `__call__` in `PlotMinNow`, it has very cheap operations, if we re-draw the chart in side `__call__`, it may be a bad idea.\n", "\n", "\n", "## Callback settings and RPCServer combination (for interfaceless)\n", "\n", "For interfaceless settings, you have 3 options on the transformer function: without callable, with callable, with optional callable. And you have the option whether to add the callback handler when you invoke the transformer. And you have the option to set the RPCServer.\n", "\n", "The following table is a full list of possible combinations and indicated scenarios\n", "\n", "| Callback in transformers | Provide callback handler when using transformers | Customize RPCServer | Scenario\n", "| :---: | :---: | :---: | :---\n", "| No callback | No | No | **Most common**, the callback feature is not used at all\n", "| No callback | No | Yes | *Meaningless*, plus you may introduce overhead to start and stop the server\n", "| No callback | Yes | No | *Meaningless*, plus you may introduce overhead to start and stop the callback handler\n", "| No callback | Yes | Yes | *Meaningless*, plus you may introduce overhead to start and stop the callback handler and the server\n", "| Required | No | No | *Invalid*, Fugue compile time exception will be thrown\n", "| Required | No | Yes | *Invalid*, Fugue compile time exception will be thrown\n", "| Required | Yes | No | **Local only**, you can use it to test your callbacks using `NativeExecutionEngine` only. If you use a distributed engine, serialization exception will be thrown\n", "| Required | Yes | Yes | **Common**, a typical way to use callbacks both locally and distributedly\n", "| Optional | No | No | **Flexible**, the callback on the transformer side will be None, you need to check. For a transformer to run with and without a callback, this is a solution\n", "| Optional | No | Yes | *Meaningless*, plus you may introduce overhead to start and stop the server\n", "| Optional | Yes | No | **Local only**, you can use it to test your callbacks using `NativeExecutionEngine` only. If you use a distributed engine, serialization exception will be thrown\n", "| Optional | Yes | Yes | **Common**, a typical way to use callbacks both locally and distributedly" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.9" } }, "nbformat": 4, "nbformat_minor": 4 }