FeatureCloud in practice

FeatureCloud as a federated platform uses docker for containerization in the app and the controller. The controller is the only local FeatureCloud component which has access to the internet and in charge of data communication between different modules. On the other hand, apps are highly isolated from accedes to local storage and internet, which introduces some practical considerations. In this story, I will discuss various minor issues that are caused by deodorization.

Data access

Architecture

FeatureCloud covers a wide range of demands in a federated platform. For instance, it provides an app store that makes it possible for developers to promote their apps there, while they can go through the certification process/es. Meanwhile, for federated collaboration, FeatureCloud introduces the controller as a component to a variety of tasks such as:

  1. Handling the workflow initiation through the UI.
  2. Checking the apps, workflow, or users statuses in FeatureCloud databases.
  3. Downloading, Running, and managing the apps in workflows

These are a part of operations that the controller handles in Featurecloud platform. Here, I will be more focused on the aspects that app developers should be aware of once they want to develop application:

Figure 1. FeatureCloud Architecture

Input and output data

As it is mentioned before, the apps are isolated and all their needs should be satisfied through the controller. All a nominal app needs are input and output files, including data, configurations, and output results. Indeed, the input should be cloned into the app’s container via the controller, and output should be downloaded from the container, once it is ready! It is crucial for the controller to be aware of where the input or output data are/ expected in the local machine. With these information, the controller cloned the input data, for each container, from the local machine into the containers space. On the other hand, once the app execution is over, it can download the results to specific directory in the local machine to make it accessible for the user. For that purpose, FeatureCloud controller automatically creates a data directory once it is started.

$ featurecloud controller start --data-dir $DATA_DIR

The controller uses DATA_DIR as a path that contains the data directory. If the path is not provided, the controller uses the current dorectory to create the data directory:

data/
├── logs
├── site_info.json
├── tests
└── workflows

By default, any controller data directory includes tests , workflows and logs to hold the test-bed output, workflow output, and logs, respectively. For the input data of apps, in the test-bed, users can create any sub-directories to keep the clients data.

Just beware that the data for workflows, i.e., projects, should be provided through the UI, and the controller uses the provided path to clone all the data to the app’s container. Also, in a workflow, the controller automatically, clone output of the last executed app in a workflow into the next app’s container.

Accordingly, I recommends followings practices for app developers to make the app development easier:

  1. Always start the controller with a specific path for the data directory.
  2. Create a folder to hold the data for your experiments.
  3. For each experiment, create sub folders to hold client specific data.
  4. Create a generic sub folder to hold the data and configurations that should be shared with all the clients, e.g., the config file.

Native mode VS container mode

Containerization

For developing a federated application in FeatureCloud platform, one should create the app using FeatureCloud pip package, implement the states, build the app, and test it in the test-bed (more detail in Create your first FeatureCloud app and Run a FeatureCloud app in the testbed stories.). After testing the app as a stand-alone app, developers should test it in possible workflows to ensure its compatibility with other apps. Consequently, during the incremental development process, developers repeatedly need to build the app image and run it in the test-bed. In this story, I discuss a more practical way to natively implement the FeatureCloud app (to most extent) natively and later test it in both test-bed and workflow.

Native mode

For running the app inside a container, the controller which has started the app and has access to it, provides followings for the app run it through using the handle_setup method inside a container:

  1. client_id : Assigned ID for the app instance.
  2. coordinator : role of the app instance. True means the coordintaor role!
  3. clients : IDs of all clients (app instances).

These information are vital for data communication.

    def handle_setup(self, client_id, coordinator, clients):
""" It will be called on startup and contains information about the
execution context of this instance. And registers all of the states.


Parameters
----------
client_id: str
coordinator: bool
clients: list

"""
self.id = client_id
self.coordinator = coordinator
self.clients = clients

self.log(f'id: {self.id}')
self.log(f'coordinator: {self.coordinator}')
self.log(f'clients: {self.clients}')

self.current_state = self.states.get('initial')

if not self.current_state:
self.log('initial state not found', level=LogLevel.FATAL)

self.thread = threading.Thread(target=self.guarded_run)
self.thread.start()

To enable the coordinator to run the app we need to instantiate the Bottlw and run run_app in main.

server = Bottle()


def run_app():
server.mount('/api', api_server)
server.mount('/web', web_server)
server.run(host='localhost', port=5000)

Now for running the app in native mode without affecting the app run in container, one can define two mode simply by checking the os.getenv("PATH_PREFIX") :

def is_native():
path_prefix = os.getenv("PATH_PREFIX")
if path_prefix:
return False
return True

if __name__ == '__main__':
app.register()
if is_native():
app.handle_setup(client_id='1', coordinator=True, clients=['1'])
else:
run_app()

Therefore, if the apps is running inside the container, the prefix will not be null and run_app() will be executed. Otherwise, by calling the handle_setup , we run the app natively. Evidently, all the input data, logs, and results will be local.

Data access in native mode

since, there is no container, for the sake of consistency and simplicity, one can create mnt/input directories to contain the data and use the same path to access it either in native mode or inside the container. For instance the tree of files for an exemplary app that supports both native and container mode, can look like as follows:

.
├── Dockerfile
├── LICENSE
├── main.py
├── mnt
│ ├── input
│ │ └── counts.tsv
│ └── output
├── README.md
├── requirements.txt
├── server_config
│ ├── docker-entrypoint.sh
│ ├── nginx
│ └── supervisord.conf
└── states.py

As you expect, the same solution works for output files. In this way, once your application is developed, and tested in the native mode, most probably, it works for one client in the test-bed.

Pros and cons

Running the app in the native mode without involving the controller imposes some limitations:

  1. Native run only supports one client. After implementing the app for one client and testing it natively, one can test it for more than one client by building the app and running it in the test bed.
  2. Native run only supports the coordinator role. If there are participant specific states, one cannot test it in native run.
  3. Native run imposes some limitations regarding data communication. send_data_to_participant is not supported because of role limitation. While, the is no controller to take care of data communication, using send_to_self argument in sending methods, makes it possible to receive data in the coordinator side. You can learn more about data communication in Communicate data across clients story.

However, because in majority of collaborations, the coordinator has data and contributes to the project with local training and computation, we rarely end-up with participant only states. Moreover, for most applications, the federated/aggregation aspects are minor and the local computation is quite similar to centralized version. Therefore, we can safely expect the natively tested app requires minor changes while it is tested in the container mode. All in all, the native mode appears quite handy for FeatureCloud newcomers to speed up the development and debugging.

If you like to check out the example app that I used here, please check out the repository on GihHub: https://github.com/FeatureCloud/native_app_dev_ex

--

--