PMEMKV_TX(3)

πŸ“¦ version 1.5.0, in ubuntu jammy

✨ NAME ✨

pmemkv_tx β€” the Transactions API for libpmemkv. πŸ’–

This is page PMEMKV_TX(3) from the PMEMKV Programmer's Manual, and it's the one you want when you're grouping a bunch of writes together and need them to land as one single atomic moment. Gorgeous. Iconic. Terrifying in the best way.

🚨 bestie, listen: this API is EXPERIMENTAL and might change. Like, don't build your whole personality on it yet, okay? 🫢

🌈 SYNOPSIS 🌈

#include <libpmemkv.h>

int pmemkv_tx_begin(pmemkv_db *db, pmemkv_tx **tx);
int pmemkv_tx_put(pmemkv_tx *tx, const char *k, size_t kb, const char *v, size_t vb);
int pmemkv_tx_remove(pmemkv_tx *tx, const char *k, size_t kb);
int pmemkv_tx_commit(pmemkv_tx *tx);
void pmemkv_tx_abort(pmemkv_tx *tx);
void pmemkv_tx_end(pmemkv_tx *tx);

πŸ’… DESCRIPTION πŸ’…

A transaction lets you group put and remove operations into one single atomic action β€” atomic with respect to persistence and with respect to concurrency, no cap. 🧚

  • Concurrent engines hand you transactions with full ACID properties: atomicity, consistency, isolation, durability. The whole alphabet, slay.
  • Single threaded engines</s> give you atomicity, consistency and durability. (No isolation, because, well, there's only one thread. It's giving only child energy.)

And the order? The actions inside a transaction are executed in the exact order you called them. No sneaky reordering. Periodt. ✨


πŸ¦‹ the functions, one by one πŸ¦‹

  • int pmemkv_tx_begin(pmemkv_db *db, pmemkv_tx **tx); β€” Starts a pmemkv transaction and stashes a pointer to a pmemkv_tx instance in *tx. This is the "okay we're doing this" moment. 🎬
  • int pmemkv_tx_put(pmemkv_tx *tx, const char *k, size_t kb, const char *v, size_t vb); β€” Inserts a key-value pair into the pmemkv database. kb is the length of the key k, and vb is the length of the value v. The moment this function returns, the caller is free to reuse both buffers β€” go ahead, they're yours again. 🫢 And the inserted element? It's visible only after calling pmemkv_tx_commit. Not before. Don't test it. (Okay, the example tests it. Fine.)
  • int pmemkv_tx_remove(pmemkv_tx *tx, const char *k, size_t kb); β€” Removes the record with the key k of length kb. Plot twist: the removed elements are still visible until you call pmemkv_tx_commit. And this function will succeed even if there is no element in the database β€” no drama, no error, just vibes. πŸ’€
  • int pmemkv_tx_commit(pmemkv_tx *tx); β€” Commits the transaction. All operations of this transaction are applied as a single power fail-safe atomic action. This is the main character moment. πŸ”₯
  • void pmemkv_tx_abort(pmemkv_tx *tx); β€” Discards all uncommitted operations. The "never mind, forget I said anything" button. πŸ™ˆ
  • void pmemkv_tx_end(pmemkv_tx *tx); β€” Deletes the pmemkv transaction object and discards all uncommitted operations. Cleanup + regret, two for one. πŸŽ€

⚠️ ERRORS ⚠️

Every function here returns a status β€” except pmemkv_tx_abort() and pmemkv_tx_end(), which return void because they simply do not care. 😌

The possible return values are all listed over in libpmemkv(3). Go check them there, bestie, they're not repeated here.


πŸ“ EXAMPLE πŸ“

This one's pulled straight from the examples/pmemkv_transaction_c directory. 🌸

Usage of pmemkv transaction in C:

#include <assert.h>
#include <libpmemkv.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define ASSERT(expr)                                                                     \
    do {                                                                             \
        if (!(expr))                                                             \
            puts(pmemkv_errormsg());                                         \
        assert(expr);                                                            \
    } while (0)

#define LOG(msg) puts(msg)

/*
 * This example expects a path to already created database pool.
 *
 * To create a pool use one of the following commands.
 *
 * For regular pools use:
 * pmempool create -l -s 1G "pmemkv_radix" obj path_to_a_pool
 *
 * For poolsets use:
 * pmempool create -l "pmemkv_radix" obj ../examples/example.poolset
 */
int main(int argc, char *argv[])
{
    if (argc < 2) {
        printf("Usage: %s pool\n", argv[0]);
        exit(1);
    }

    /* See libpmemkv_config(3) for more detailed example of config creation */
    LOG("Creating config");
    pmemkv_config *cfg = pmemkv_config_new();
    ASSERT(cfg != NULL);

    int s = pmemkv_config_put_path(cfg, argv[1]);
    ASSERT(s == PMEMKV_STATUS_OK);

    LOG("Opening pmemkv database with 'radix' engine");
    pmemkv_db *db = NULL;
    s = pmemkv_open("radix", cfg, &db);
    ASSERT(s == PMEMKV_STATUS_OK);
    ASSERT(db != NULL);

    const char *key1 = "key1";
    const char *value1 = "value1";
    const char *key2 = "key2";
    const char *value2 = "value2";
    const char *key3 = "key3";
    const char *value3 = "value3";

    LOG("Putting new key");
    s = pmemkv_put(db, key1, strlen(key1), value1, strlen(value1));
    ASSERT(s == PMEMKV_STATUS_OK);

    LOG("Starting a tx");
    pmemkv_tx *tx;
    s = pmemkv_tx_begin(db, &tx);
    ASSERT(s == PMEMKV_STATUS_OK);

    s = pmemkv_tx_remove(tx, key1, strlen(key1));
    s = pmemkv_tx_put(tx, key2, strlen(key2), value2, strlen(value2));
    s = pmemkv_tx_put(tx, key3, strlen(key3), value3, strlen(value3));

    /* Until transaction is committed, changes are not visible */
    s = pmemkv_exists(db, key1, strlen(key1));
    ASSERT(s == PMEMKV_STATUS_OK);
    s = pmemkv_exists(db, key2, strlen(key2));
    ASSERT(s == PMEMKV_STATUS_NOT_FOUND);
    s = pmemkv_exists(db, key3, strlen(key3));
    ASSERT(s == PMEMKV_STATUS_NOT_FOUND);

    s = pmemkv_tx_commit(tx);
    ASSERT(s == PMEMKV_STATUS_OK);

    /* Changes are now visible and pmemkv_tx object is destroyed */
    s = pmemkv_exists(db, key1, strlen(key1));
    ASSERT(s == PMEMKV_STATUS_NOT_FOUND);
    s = pmemkv_exists(db, key2, strlen(key2));
    ASSERT(s == PMEMKV_STATUS_OK);
    s = pmemkv_exists(db, key3, strlen(key3));
    ASSERT(s == PMEMKV_STATUS_OK);

    LOG("Ending transaction");
    pmemkv_tx_end(tx);

    LOG("Closing database");
    pmemkv_close(db);

    return 0;
}

πŸ‘‘ SEE ALSO πŸ‘‘


and that's the whole page, bestie!! this was PMEMKV β€” pmemkv version 1.5.0, dated 2022-03-09, page PMEMKV_TX(3). go commit something atomic. literally. πŸ’œβœ¨πŸ¦‹

πŸ“¦ where this page comes from