Client API

The Flock client API allows external processes to query group membership and interact with Flock providers without being group members themselves.

Overview

The client API provides:

  • Group handle creation: Connect to a set of Flock providers

  • View querying: Retrieve the current group membership

  • Member discovery: Find members by rank or address

  • Metadata access: Read group metadata

This is useful for:

  • Load balancers that need to discover service instances

  • Monitoring tools that track group membership

  • Clients that need to connect to any group member

Basic client usage

Here’s a complete example of using the Flock client API:

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

int main(int argc, char** argv)
{
    if(argc != 3) {
        fprintf(stderr, "Usage: %s <server address> <provider id>\n", argv[0]);
        exit(-1);
    }

    flock_return_t ret;
    const char* svr_addr_str = argv[1];
    uint16_t provider_id = atoi(argv[2]);

    /* Initialize Margo in client mode */
    margo_instance_id mid = margo_init("na+sm", MARGO_CLIENT_MODE, 0, 0);
    assert(mid);

    margo_set_log_level(mid, MARGO_LOG_INFO);

    /* Lookup server address */
    hg_addr_t svr_addr;
    margo_addr_lookup(mid, svr_addr_str, &svr_addr);

    /* Create Flock client */
    flock_client_t client;
    ret = flock_client_init(mid, ABT_POOL_NULL, &client);
    if(ret != FLOCK_SUCCESS) {
        fprintf(stderr, "flock_client_init failed\n");
        return -1;
    }

    /* Create group handle */
    flock_group_handle_t group_handle;
    ret = flock_group_handle_create(client, svr_addr, provider_id, true, &group_handle);
    if(ret != FLOCK_SUCCESS) {
        fprintf(stderr, "flock_group_handle_create failed\n");
        return -1;
    }

    /* Query group membership */
    flock_group_view_t view = FLOCK_GROUP_VIEW_INITIALIZER;
    ret = flock_group_get_view(group_handle, &view);
    if(ret != FLOCK_SUCCESS) {
        fprintf(stderr, "flock_group_handle_get_view failed\n");
        return -1;
    }

    printf("Group has %zu members:\n", view.members.size);
    for(size_t i = 0; i < view.members.size; i++) {
        printf("  [%zu] %s (provider_id=%d)\n",
               i, view.members.data[i].address,
               view.members.data[i].provider_id);
    }

    /* Clean up */
    flock_group_view_clear(&view);
    flock_group_handle_release(group_handle);
    flock_client_finalize(client);
    margo_addr_free(mid, svr_addr);
    margo_finalize(mid);

    return 0;
}

Client initialization

Create a client:

The client is created using flock_client_init:

  • mid: Margo instance

  • pool: Argobots pool (currently unused)

  • client: Pointer to store the client handle

Finalize the client:

Use flock_client_finalize(client) to clean up resources associated with the client.

Group handle operations

Create a group handle:

Use flock_group_handle_create with:

  • client: The Flock client

  • svr_addr: Mercury address of a group member

  • provider_id: Provider ID of that member

  • refresh: Whether to auto-refresh the view

  • group_handle: Pointer to store the handle

The refresh parameter controls whether the handle needs to contact the group to get the most recent view.

Create from a file:

You can also create a group handle from a serialized view file:

flock_group_handle_t group_handle;
flock_return_t ret = flock_group_handle_create_from_file(
    client, "mygroup.flock", refresh, &group_handle);

This loads the view from the file and creates handles to the members.

Release a group handle:

Use flock_group_handle_release(group_handle) when done.

Querying group membership

Get the full view:

flock_group_view_t view = FLOCK_GROUP_VIEW_INITIALIZER;
flock_return_t ret = flock_group_get_view(group_handle, &view);

// Use the view
printf("Group size: %zu\n", view.members.size);

// Clean up
flock_group_view_clear(&view);

This creates a copy of the current group view. You own this copy and must call flock_group_view_clear to free it.

Important

flock_group_get_view does NOT issue an RPC to get the current view. If you expect the view to have changed, call flock_group_update_view and flock_request_wait on the resulting request.

Error handling

Always check return values:

flock_return_t ret = flock_client_init(mid, ABT_POOL_NULL, &client);
if(ret != FLOCK_SUCCESS) {
    fprintf(stderr, "Failed to initialize client: %d\n", ret);
    return -1;
}

Common error codes:

  • FLOCK_SUCCESS: Operation succeeded

  • FLOCK_ERR_INVALID_ARG: Invalid argument

  • FLOCK_ERR_MERCURY: Mercury error

  • FLOCK_ERR_NOT_FOUND: Member not found