Bootstrap method: join

The “join” bootstrap method allows a process to dynamically join an existing Flock group. Unlike the “file” or “view” bootstrap methods which simply load a static view, “join” actively contacts existing group members and requests to be added to the group.

When to use

Use the “join” bootstrap method when:

  • You want to dynamically add processes to a running group

  • You’re implementing elastic services that scale up at runtime

  • You need existing group members to be notified of new members

  • You want the group view to be updated across all members

Prerequisites

To use the join bootstrap method, you need:

  • An existing Flock group with at least one member

  • A group view file containing addresses of current members

  • A backend that supports dynamic membership (use “centralized”, not “static”)

How it works

The join process:

  1. The new process loads a view (e.g. from a file) containing addresses of existing group members

  2. It registers a provider with "bootstrap": "join" in the configuration

  3. The provider contacts existing members and requests to join

  4. The existing members add the new member to the group

  5. All members receive an updated view with the new member

This is different from the “file” bootstrap, which simply loads a view that we expect the process to already be part of.

Configuration

In Bedrock configuration:

{
    "libraries": [
        "libflock-bedrock-module.so"
    ],
    "providers": [
        {
            "type": "flock",
            "name": "my_flock_provider",
            "provider_id": 42,
            "config": {
                "bootstrap": "join",
                "file": "mygroup.flock",
                "group": {
                    "type": "centralized",
                    "config": {}
                }
            }
        }
    ]
}

The configuration specifies:

  • bootstrap: Must be “join” to enable dynamic joining

  • file: Path to a file containing the current group view

  • group.type: Must support dynamic membership (e.g. not be “static”)

In C code

To join a group programmatically:

/*
 * (C) 2024 The University of Chicago
 *
 * See COPYRIGHT in top-level directory.
 */
#include <assert.h>
#include <stdio.h>
#include <margo.h>
#include <flock/flock-server.h>
#include <flock/flock-bootstrap.h>

int main(int argc, char** argv)
{
    if(argc != 2) {
        fprintf(stderr, "Usage: %s <group_file>\n", argv[0]);
        return -1;
    }

    const char* group_file = argv[1];

    // Initialize Margo
    margo_instance_id mid = margo_init("na+sm", MARGO_SERVER_MODE, 0, 0);
    assert(mid);

    // Print server address
    hg_addr_t my_address;
    margo_addr_self(mid, &my_address);
    char addr_str[256];
    hg_size_t addr_str_size = 256;
    margo_addr_to_string(mid, addr_str, &addr_str_size, my_address);
    margo_addr_free(mid, my_address);

    printf("Server running at address %s\n", addr_str);

    // Initialize provider args
    struct flock_provider_args args = FLOCK_PROVIDER_ARGS_INIT;
    flock_group_view_t initial_view = FLOCK_GROUP_VIEW_INITIALIZER;
    args.initial_view = &initial_view;

    uint16_t provider_id = 42;
    int ret;

    // Load the existing group view from file
    // This view contains the addresses of current group members
    ret = flock_group_view_init_from_file(group_file, &initial_view);
    if(ret != FLOCK_SUCCESS) {
        fprintf(stderr, "Failed to load group view from file: %s\n", group_file);
        margo_finalize(mid);
        return -1;
    }

    printf("Loaded group view from file: %s\n", group_file);
    printf("Current group size: %zu\n", initial_view.members.size);

    // Configure with centralized backend and "join" bootstrap method
    // The "join" bootstrap tells the provider to contact existing members
    // and request to join the group dynamically
    const char* config =
        "{"
        "  \"bootstrap\": \"join\","
        "  \"group\": {"
        "    \"type\": \"centralized\","
        "    \"config\": {}"
        "  }"
        "}";

    // Register provider - it will join the existing group
    flock_provider_t provider;
    ret = flock_provider_register(mid, provider_id, config, &args, &provider);
    if(ret != FLOCK_SUCCESS) {
        fprintf(stderr, "Failed to register provider and join group\n");
        flock_group_view_clear(&initial_view);
        margo_finalize(mid);
        return -1;
    }

    printf("Successfully joined the group\n");

    // Wait for finalize
    margo_wait_for_finalize(mid);

    return 0;
}

The key steps are:

  1. Load the existing group view from a file using flock_group_view_init_from_file

  2. Set "bootstrap": "join" in the configuration JSON

  3. Call flock_provider_register with the configuration and initial view

The provider will then contact the members listed in the view and request to join the group.

Example workflow

Step 1: Start the initial group member with “self” bootstrap:

$ ./initial_server
Server running at address na+sm://12345-0
Flock provider registered
Group file written to: mygroup.flock

Step 2: A new process joins using the group file:

$ ./05_flock_server mygroup.flock
Server running at address na+sm://12346-0
Loaded group view from file: mygroup.flock
Current group size: 1
Successfully joined the group

After joining, all members will have an updated view containing both members.