Basics of accessing a database

Having started a provider with a database, we are now ready for clients to connect and access it. The following program showcases the use of the database handle in the client library to access single key/value pairs.

client.c (show/hide)

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <margo.h>
#include <yokan/client.h>
#include <yokan/database.h>

static yk_return_t keyvalue_callback(void*, size_t, const void*, size_t, const void*, size_t);

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

    uint16_t provider_id = atoi(argv[2]);
    hg_addr_t server_addr = HG_ADDR_NULL;
    hg_return_t hret = margo_addr_lookup(mid, argv[1], &server_addr);
    assert(hret == HG_SUCCESS);

    yk_return_t ret;
    yk_client_t client = YOKAN_CLIENT_NULL;

    ret = yk_client_init(mid, &client);
    assert(ret == YOKAN_SUCCESS);

    yk_database_handle_t db_handle = YOKAN_DATABASE_HANDLE_NULL;
    ret = yk_database_handle_create(
        client, server_addr, provider_id, true, &db_handle);
    assert(ret == YOKAN_SUCCESS);

    const char* key = "matthieu";
    const char* value_in = "dorier";

    /* putting a key/value pair */
    ret = yk_put(db_handle, YOKAN_MODE_DEFAULT,
                 key, strlen(key), value_in, strlen(value_in));
    assert(ret == YOKAN_SUCCESS);

    /* getting the current number of stored key/value pairs */
    size_t count;
    ret = yk_count(db_handle, YOKAN_MODE_DEFAULT, &count);

    /* checking that the key exists */
    uint8_t flag;
    ret = yk_exists(db_handle, YOKAN_MODE_DEFAULT,
                    key, strlen(key), &flag);
    assert(ret == YOKAN_SUCCESS);
    assert(flag);

    /* getting the length of the value associated with the key */
    size_t vsize;
    ret = yk_length(db_handle, YOKAN_MODE_DEFAULT,
                    key, strlen(key), &vsize);

    /* getting the value associated with a key */
    char* value_out = malloc(vsize);
    size_t value_out_size = vsize;
    ret = yk_get(db_handle, YOKAN_MODE_DEFAULT,
                 key, strlen(key), value_out, &value_out_size);
    assert(ret == YOKAN_SUCCESS);
    free(value_out);

    /* getting the value associated with a key using a callback */
    ret = yk_fetch(db_handle, YOKAN_MODE_DEFAULT,
                   key, strlen(key), keyvalue_callback, NULL);
    assert(ret == YOKAN_SUCCESS);

    /* deleting a key/value pair */
    ret = yk_erase(db_handle, YOKAN_MODE_DEFAULT, key, strlen(key));

    ret = yk_database_handle_release(db_handle);
    assert(ret == YOKAN_SUCCESS);

    ret = yk_client_finalize(client);
    assert(ret == YOKAN_SUCCESS);

    margo_finalize(mid);

    return 0;
}

static yk_return_t keyvalue_callback(
        void* uargs, size_t i,
        const void* key, size_t ksize,
        const void* val, size_t vsize) {
    (void)uargs;
    (void)i;
    printf("Inside callback: %.*s => %.*s\n",
           (int)ksize, (const char*)key, (int)vsize, (const char*)val);
}

The client library provides the following functions for accessing single key/value pairs:

  • yk_put: puts a key/value pair into the database.

  • yk_count: counts the number of key/value pairs currently in the database.

  • yk_exists: checks whether the given key is in the database.

  • yk_length: gets the length of the value associated with a given key.

  • yk_get: gets the value associated with the given key.

  • yk_fetch: gets the value associated with the given key using a functional style.

  • yk_erase: erases the key/value pair associated with the given key.

These functions are extensively documented in the yokan/database.h header.

If your application accesses a large number of key/value pairs and can batch them together, we recommend using the functions presented in the next tutorial to improve performance.