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 instancepool: 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 clientsvr_addr: Mercury address of a group memberprovider_id: Provider ID of that memberrefresh: Whether to auto-refresh the viewgroup_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 succeededFLOCK_ERR_INVALID_ARG: Invalid argumentFLOCK_ERR_MERCURY: Mercury errorFLOCK_ERR_NOT_FOUND: Member not found