cpp50Example_main.cpp

A simple performance test for basic RDM Core functions. This example needs a compiled schema, cpp50.sdl.

/*
* Raima Database Manager
*
* Copyright (C) 2020 Raima Inc., All rights reserved.
*
* Use of this software, whether in source code format, or in executable,
* binary object code form, is governed by the Raima LICENSE which
* is fully described in the LICENSE.TXT file, included within this
* distribution of files.
*/
/* \file cpp50_main.cpp
* \brief Source code for a simple performance example
*
* Below is a simple performance example. It uses the C API.
* However, this code is in C++ as two C++ classes and the standard
* C++ libarary are used for I/O.
*
* All the memory used by RDM is provided by a buffer compiled into
* this application (static char buf[MEM_SIZE]) except for possibly
* some memory allocated by system functions used by RDM for the
* implementation of the Platform Support Layer (PSP).
*
* There is three main parts to this performance example.
*
* First we do a number of operations where we do not guarantee
* database durability:
*
* - Initialize the database
*
* - Insert 1 million entries with a transaction size of 1000
*
* - Lookup 1000 random rows from the data inserted above
*
* - Loopup 1000 random rows where 90% of them does not exist and
* 10% from the data inserted above
*
* - Update the database by adding 1000 new entries
*
* - Update the database by deleting 1000 random entries
*
* Then the second part where we are fully durable:
*
* - Update the database by adding 1000 new entries
*
* - Update the database by deleting 1000 random entries
*
*
* The third part where we do inserts and lookups in parallel where
* we are not durable:
*
* - 1000 inserts, deletes, and reads in parallel using locks
*
* - 1000 inserts, deletes, and reads in parallel using snapshots
*
* - Clean up
*
* Performance numbers are printed out in the following format:
*
* AVERAGE +/- DEV [MIN, MAX], n: #, total: TOTAL, DESCRIPTION
*
* Please be aware that the schema does not have any explicit keys.
* We instead utilize the row-ID where we explicitly assign it a hash
* of column c2. However, if we get a collision we increment one to
* the hash. This is done by using a mask when we hash. That mask
* have space in the least significants bits for duplicates. We make
* sure to use a mask that limit it's size for more efficient
* encoding.
*/
#include "sw.h"
#include "rand.h"
#include "hash.h"
#include "thread_api.h"
#include "rdmtfsapi.h"
#include "rdmdbapi.h"
#include "rdmcursorapi.h"
#include "rdmcmdlineapi.h"
#include "rdmstartupapi.h"
#include "cpp50_structs.h"
#include "cpp50_cat.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
using namespace RDM_CPP::CPP50;
uint32_t total_number_of_rows;
uint32_t rows_per_transaction;
/* \brief Standard error print macro */
#define print_error(rc) print_errorEx (rc, __FILE__, __LINE__)
/* \brief Standard error print format
*
* This demonstrates the usage of the rdm_retcodeGetName() and
* rdm_retcodeGetDescription() functions.
*/
static void print_errorEx (
RDM_RETCODE rc, /*< [in] RDM_RETCODE to analyze */
const char *file, /*< [in] Sourcec filename */
int line /*< [in] Lineno in source file */
)
{
if (rc != sOKAY)
{
cerr << file << ":" << line << ": error: " <<
rdm_retcodeGetName (rc) << " (" << rc << "): " << rdm_retcodeGetDescription (rc) << endl;
}
}
/* Hash mask where masked numbers can always be endoded in 5 bytes */
const uint64_t hash_mask = 0X7FFFFFF00ULL;
/* Number of duplicates allowed. This is derived from the hash mask */
const uint64_t max_hash_duplicates = ((~hash_mask + 1) & hash_mask);
/* \brief Generate data
*
* Generate sample data for the purpose of inserting it into the
* database or we can use this for looking up data that have already
* been inserted. Looking up existing data has to use a pseudo random
* generator that is able to reproduce the original pseudo random
* sequence.
*/
static void genData (
SAMPLE &sample, /*< [out] The sample to be generated */
RDM_ROWID_T &hash, /*< [out] The rowId to use for this row unless there is collisions */
RAND &rand /*< [in] The pseudo random number generator to use */
)
{
sample.c1 = rand.draw (10000, 9999);
/* Inserts are more efficient if the key values are in order */
for (int j = 0; j < 44; j++)
{
sample.c2[j] = rand.draw (33, 126);
}
hash = rdm_hash ((uint8_t *) sample.c2, sizeof (sample.c2)) & hash_mask;
sample.c3 = rand.draw (1000000000, 4200000000);
}
/* \brief Compare key values
*
* Compare the two key values. Return 0 if they are the same, 1 otherwise.
*/
static int cmpData (
uint8_t data1[44], /*< [in] The first data to be compared */
uint8_t data2[44] /*< [in] The second data to be compared */
)
{
return memcmp (data1, data2, 44);
}
/* \brief Lookup row
*
* Lookup a row where we handle hash collision.
*
* @return Returns sOKAY if the row is found and sNOTFOUND if the row does not exist.
*/
static RDM_RETCODE lookupRow (RDM_CURSOR cursor, /* [in] The cursor we use to navigate to the row using a row scan */
uint8_t key[44], /* [in] The key value for the row to be looked up */
RDM_ROWID_T hash, /* [in] The hash of the key we want to look up */
SAMPLE *sample /* [out] The sample date to be returned back to the caller */
)
{
/* Actual hash is always at least one larger than the base hash */
RDM_RETCODE rc = rdm_cursorMoveToRowId (cursor, hash + 1);
if (rc == sNOTFOUND)
{
rc = rdm_cursorMoveToNext (cursor);
}
if (rc == sOKAY)
{
rc = rdm_cursorReadRow (cursor, sample, sizeof(*sample), NULL);
for (uint64_t count = 0; rc == sOKAY && cmpData (sample->c2, key) != 0; count++)
{
RDM_ROWID_T currentHash;
rdm_cursorGetRowId (cursor, &currentHash);
if ((currentHash & hash_mask) != hash)
{
rc = sNOTFOUND;
}
if (rc == sOKAY)
{
rc = rdm_cursorMoveToNext (cursor);
}
if (rc == sOKAY)
{
rc = rdm_cursorReadRow (cursor, sample, sizeof(*sample), NULL);
}
}
}
if (rc == sENDOFCURSOR)
{
rc = sNOTFOUND;
}
return rc;
}
/* \brief Get the actual hash of a new row
*
* Get the actual hash of a new row we are about to insert. If the
* row already exist we will position the cursor to this row.
*
* @return Returns sOKAY if the row does not exist and sDUPLICATE if
* the row already exist. However, if the possible actual hash values
* is exhausted we will return eNOSPACE.
*/
static RDM_RETCODE actualHashOfRow (RDM_CURSOR cursor, /* [in] The cursor we use to navigate to the row using a row scan */
uint8_t key[44], /* [in] The key value for the row to be looked up */
RDM_ROWID_T hash, /* [in] The hash of the key we want to look up */
RDM_ROWID_T *pActualHash /* [out] Actual hash to use for an insert */
)
{
RDM_ROWID_T actualHashCandidate = hash + 1;
RDM_BOOL_T actualHashCandidateFound = RDM_FALSE;
rc = rdm_cursorMoveToRowId (cursor, actualHashCandidate);
if (rc == sNOTFOUND)
{
actualHashCandidateFound = RDM_TRUE;
rc = rdm_cursorMoveToNext (cursor);
}
if (rc == sOKAY)
{
SAMPLE sample;
rc = rdm_cursorReadRow (cursor, &sample, sizeof(sample), NULL);
if (rc == sOKAY)
{
}
while (rc == sDUPLICATE && cmpData (sample.c2, key) != 0)
{
RDM_ROWID_T currentHash;
rc = rdm_cursorGetRowId (cursor, &currentHash);
if (rc == sOKAY && actualHashCandidateFound == RDM_FALSE)
{
if (currentHash > actualHashCandidate)
{
actualHashCandidateFound = RDM_TRUE;
}
}
if (rc == sOKAY && ((currentHash & hash_mask) == hash))
{
rc = rdm_cursorMoveToNext (cursor);
if (actualHashCandidateFound == RDM_FALSE)
{
actualHashCandidate ++;
}
if (rc == sOKAY)
{
rc = rdm_cursorReadRow (cursor, &sample, sizeof(sample), NULL);
}
if (rc == sOKAY)
{
}
}
else if (actualHashCandidateFound == RDM_FALSE)
{
rc = eNOSPACE;
}
}
if (rc == sENDOFCURSOR)
{
actualHashCandidateFound = RDM_TRUE;
rc = sOKAY;
}
}
else if (rc == sENDOFCURSOR)
{
rc = sOKAY;
}
if (rc == sOKAY)
{
*pActualHash = actualHashCandidate;
}
return rc;
}
/* \brief Insert row
*
* Insert a row where we handle hash collision.
*
* @return Returns sOKAY for a success insert and sDUPLICATE if the row already exist.
*/
static RDM_RETCODE insertRow (RDM_DB db, /*< [in] RDM db handle with the database open */
RDM_CURSOR lookupCursor, /* [in] Temporary cursor to look up for duplicates */
SAMPLE *sample, /* [in] The sample data to be inserted */
RDM_ROWID_T hash, /* [in] The hash of the key we want to look up */
RDM_CURSOR *pCursor /* [out] The cursor for newly inserted row we return back to the user. */
)
{
RDM_ROWID_T actualHash;
RDM_RETCODE rc = actualHashOfRow (lookupCursor, sample->c2, hash, &actualHash);
if (rc == sOKAY)
{
rc = rdm_dbReinsertRow (db, TABLE_SAMPLE, actualHash, sample, sizeof (SAMPLE), pCursor);
}
return rc;
}
/* \brief Insert or update row
*
* Insert or update row where we handle hash collision.
*
* @return Returns sOKAY for success
*/
static RDM_RETCODE insertOrUpdateRow (RDM_DB db, /*< [in] RDM db handle with the database open */
RDM_CURSOR lookupCursor, /* [in] Cursor will be positioned at the row we inserted or updated */
SAMPLE *sample, /* [in] The sample data to be inserted */
RDM_ROWID_T hash /* [in] The hash of the key we want to look up */
)
{
RDM_ROWID_T actualHash;
RDM_RETCODE rc = actualHashOfRow (lookupCursor, sample->c2, hash, &actualHash);
if (rc == sOKAY)
{
RDM_CURSOR cursor = NULL;
rc = rdm_dbReinsertRow (db, TABLE_SAMPLE, actualHash, sample, sizeof (SAMPLE), &cursor);
if (rc == sOKAY)
{
rc = rdm_cursorMoveToPosition (lookupCursor, cursor);
}
rdm_cursorFree (cursor);
}
else if (rc == sDUPLICATE)
{
rc = rdm_cursorUpdateRow (lookupCursor, sample, sizeof (SAMPLE));
}
return rc;
}
/* \brief Insert or update row
*
* Insert or update row where we handle hash collision.
*
* @return Returns sOKAY for success and sNOTFOUND if the row does not exist
*/
static RDM_RETCODE updateRow (RDM_CURSOR lookupCursor, /* [in] Temporary cursor to look up for duplicates */
SAMPLE *sample, /* [in] The sample data to be inserted */
RDM_ROWID_T hash /* [in] The hash of the key we want to look up */
)
{
RDM_ROWID_T actualHash;
SAMPLE lookupSample;
RDM_RETCODE rc = lookupRow (lookupCursor, sample->c2, hash, &lookupSample);
if (rc == sOKAY)
{
rc = rdm_cursorUpdateRow (lookupCursor, sample, sizeof (SAMPLE));
}
return rc;
}
/* \brief Delete a row
*
* Delete a row where we handle hash collisions.
*
* @return Returns sOKAY for success and sNOTFOUND if the row does not exist
*/
static RDM_RETCODE deleteRow (RDM_CURSOR cursor, /* [in] The cursor we use to navigate to the row using a row scan */
uint8_t key[44], /* [in] The key value for the row to be deleted */
RDM_ROWID_T hash /* [in] The hash of the key we want to delete */
)
{
SAMPLE sample;
RDM_RETCODE rc = lookupRow (cursor, key, hash, &sample);
if (rc == sOKAY)
{
rc = rdm_cursorDeleteRow (cursor);
}
return rc;
}
/* \brief Insert data
*
* Insert data wihout any reporting.
*
* @return Returns an \c RDM_RETCODE code (\b sOKAY if successful)
*/
static RDM_RETCODE insertData (
RDM_DB db, /*< [in] RDM db handle with the database open */
RAND &rand /*< [in] The pseudo random number generator to use */
)
{
RDM_CURSOR lookupCursor = NULL;
RDM_RETCODE rc = rdm_dbGetRows (db, TABLE_SAMPLE, &lookupCursor);
for (int i = 0; rc == sOKAY && i < rows_per_transaction; i++)
{
SAMPLE sample;
genData (sample, hash, rand);
rc = insertRow (db, lookupCursor, &sample, hash, NULL);
if (rc == sDUPLICATE)
{
/* Ignore duplicates */
rc = sOKAY;
}
}
rdm_cursorFree (lookupCursor);
return rc;
}
/* \brief Insert all the data
*
* Inserts the main bulk of the data. It does this by starting a
* number of update transactions and for each of these calls
* insertData(). Performance number are reported for each of its
* operations.
*
* @return Returns an \c RDM_RETCODE code (\b sOKAY if successful)
*/
static RDM_RETCODE insertAllData (
RDM_DB db, /*< [in] RDM db handle with the database open */
string mode, /*< The durability mode to use. Can be "durable" or "consistent" */
RAND &rand /*< [in] The pseudo random number generator to use */
)
{
char sRows_per_transaction[12];
sprintf (sRows_per_transaction, "%d", rows_per_transaction);
SW swe ("Insert data - End");
SW swi ("Insert data - Insert " + string (sRows_per_transaction) + " rows");
SW sws ("Insert data - Start Update");
RDM_RETCODE rc = rdm_dbSetOption (db, "durability", mode.c_str ());
for (int i = 0; rc == sOKAY && i < total_number_of_rows; i+= rows_per_transaction)
{
RDM_TABLE_ID tables_to_lock[] = {TABLE_SAMPLE};
RDM_TRANS trans;
sws.start ();
rc = rdm_dbStartUpdate (db, tables_to_lock, RDM_LEN (tables_to_lock), NULL, 0, &trans);
sws.stop ();
if (rc == sOKAY)
{
swi.start ();
rc = insertData (db, rand);
if (rc == sOKAY)
{
swi.stop ();
swe.start ();
rc = rdm_transEnd (trans);
swe.stop ();
}
else
{
(void) rdm_transEndRollback (trans);
}
}
}
return rc;
}
/* \brief Read the data
*
* Read some of the data and report performance numbers.
*
* @return Returns an \c RDM_RETCODE code (\b sOKAY if successful)
*/
static RDM_RETCODE readData (
RDM_DB db, /*< [in] RDM db handle with the database open */
RDM_BOOL_T snapshot, /*< [in] Use a snapshot for the reads */
RAND &rand /*< [in] The pseudo random number generator to use */
)
{
RDM_CURSOR cursor = NULL;
RDM_TABLE_ID tables_to_lock[] = {TABLE_SAMPLE};
RDM_TRANS trans;
SW swe ("Read data - End");
SW swr ("Read data - Read 1 row");
SW sws ("Read data - Start Read");
for (int i = 0; rc == sOKAY && i < 1; i++)
{
sws.start ();
if (snapshot)
{
rc = rdm_dbStartSnapshot (db, tables_to_lock, RDM_LEN (tables_to_lock), &trans);
}
else
{
rc = rdm_dbStartRead (db, tables_to_lock, RDM_LEN (tables_to_lock), &trans);
}
if (rc == sOKAY)
{
rc = rdm_dbGetRows (db, TABLE_SAMPLE, &cursor);
sws.stop ();
for (int i = 0; rc == sOKAY && i < rows_per_transaction; i++)
{
SAMPLE lookup;
genData (lookup, hash, rand);
SAMPLE sample;
swr.start ();
rc = lookupRow (cursor, lookup.c2, hash, &sample);
swr.stop ();
}
if (rc == sOKAY)
{
swe.start ();
rc = rdm_transEnd (trans);
swe.stop ();
}
else
{
(void) rdm_transEnd (trans);
}
}
}
rdm_cursorFree (cursor);
return rc;
}
/* \brief Read the data where 10% exist
*
* Read some of the data and report performance numbers.
*
* @return Returns an \c RDM_RETCODE code (\b sOKAY if successful)
*/
static RDM_RETCODE readData9010 (
RDM_DB db, /*< [in] RDM db handle with the database open */
RDM_BOOL_T snapshot, /*< [in] Use a snapshot for the reads */
RAND &rand90, /*< [in] The pseudo random number generator to use for data that does not exist */
RAND &rand10 /*< [in] The pseudo random number generator to use for data that exist */
)
{
RDM_CURSOR cursor = NULL;
RDM_TABLE_ID tables_to_lock[] = {TABLE_SAMPLE};
RDM_TRANS trans;
SW swe ("Read data - End");
SW swr10 ("Read data - Read 1 row that exist");
SW swr90 ("Read data - Try to read 1 row that does not exist");
SW sws ("Read data - Start Read");
for (int i = 0; rc == sOKAY && i < 1; i++)
{
sws.start ();
if (snapshot)
{
rc = rdm_dbStartSnapshot (db, tables_to_lock, RDM_LEN (tables_to_lock), &trans);
}
else
{
rc = rdm_dbStartRead (db, tables_to_lock, RDM_LEN (tables_to_lock), &trans);
}
if (rc == sOKAY)
{
rc = rdm_dbGetRows (db, TABLE_SAMPLE, &cursor);
sws.stop ();
for (int i = 0; rc == sOKAY || rc == sNOTFOUND && i < rows_per_transaction; i++)
{
SAMPLE lookup;
RDM_BOOL_T exist = (i % 10) == 0 ? RDM_TRUE : RDM_FALSE;
RAND &rand = (exist == RDM_TRUE) ? rand10 : rand90;
SW &swr= (exist == RDM_TRUE) ? swr10 : swr90;
genData (lookup, hash, rand);
SAMPLE sample;
swr.start ();
rc = lookupRow (cursor, lookup.c2, hash, &sample);
swr.stop ();
}
if (rc == sOKAY || rc == sNOTFOUND)
{
swe.start ();
rc = rdm_transEnd (trans);
swe.stop ();
}
else
{
(void) rdm_transEnd (trans);
}
}
}
rdm_cursorFree (cursor);
return rc;
}
/* \brief Modify data with inserts
*
* Update data by inserting some additional rows. Performance number
* are reported for each of its operations.
*
* @return Returns an \c RDM_RETCODE code (\b sOKAY if successful)
*/
static RDM_RETCODE modifyDataWithInserts (
RDM_DB db, /*< [in] RDM db handle with the database open */
string mode, /*< The durability mode to use. Can be "durable" or "consistent" */
RAND &rand /*< [in] The pseudo random number generator to use */
)
{
RDM_TABLE_ID tables_to_lock[] = {TABLE_SAMPLE};
RDM_TRANS trans;
SW swe ("Insert data (" + mode + ") - End");
SW swi ("Insert data (" + mode + ") - Insert 1 row");
SW sws ("Insert data (" + mode + ") - Start Update");
RDM_RETCODE rc = rdm_dbSetOption (db, "durability", mode.c_str ());
RDM_CURSOR lookupCursor = NULL;
if (rc == sOKAY)
{
rc = rdm_dbGetRows (db, TABLE_SAMPLE, &lookupCursor);
}
for (int i = 0; rc == sOKAY && i < 1; i++)
{
sws.start ();
rc = rdm_dbStartUpdate (db, tables_to_lock, RDM_LEN (tables_to_lock), NULL, 0, &trans);
sws.stop ();
if (rc == sOKAY)
{
for (int i = 0; rc == sOKAY && i < rows_per_transaction; i++)
{
SAMPLE sample;
genData (sample, hash, rand);
swi.start ();
rc = insertRow (db, lookupCursor, &sample, hash, NULL);
if (rc == sOKAY)
{
swi.stop ();
}
}
if (rc == sOKAY)
{
swe.start ();
rc = rdm_transEnd (trans);
swe.stop ();
}
else
{
(void) rdm_transEndRollback (trans);
}
}
}
rdm_cursorFree (lookupCursor);
return rc;
}
/* \brief Modify data with deletes
*
* Update data by deleting some rows. Performance number are reported
* for each of its operations.
*
* @return Returns an \c RDM_RETCODE code (\b sOKAY if successful)
*/
static RDM_RETCODE modifyDataWithDeletes (
RDM_DB db, /*< [in] RDM db handle with the database open */
string mode, /*< The durability mode to use. Can be "durable" or "consistent" */
RAND &rand /*< [in] The pseudo random number generator to use */
)
{
RDM_CURSOR cursor = NULL;
RDM_TABLE_ID tables_to_lock[] = {TABLE_SAMPLE};
RDM_TRANS trans;
SW swe ("Delete data (" + mode + ") - End");
SW swi ("Delete data (" + mode + ") - Delete 1 row");
SW sws ("Delete data (" + mode + ") - Start Update");
RDM_RETCODE rc = rdm_dbSetOption (db, "durability", mode.c_str ());
for (int i = 0; rc == sOKAY && i < 1; i++)
{
sws.start ();
rc = rdm_dbStartUpdate (db, tables_to_lock, RDM_LEN (tables_to_lock), NULL, 0, &trans);
if (rc == sOKAY)
{
rc = rdm_dbGetRows (db, TABLE_SAMPLE, &cursor);
sws.stop ();
for (int i = 0; rc == sOKAY && i < rows_per_transaction; i++)
{
SAMPLE lookup;
genData (lookup, hash, rand);
swi.start ();
rc = deleteRow (cursor, lookup.c2, hash);
swi.stop ();
}
if (rc == sOKAY)
{
swe.start ();
rc = rdm_transEnd (trans);
swe.stop ();
}
else
{
(void) rdm_transEndRollback (trans);
}
}
}
rdm_cursorFree (cursor);
return rc;
}
/* \brief Modify data with updates of existing rows
*
* Update data by updating some rows. Performance number are reported
* for each of its operations.
*
* @return Returns an \c RDM_RETCODE code (\b sOKAY if successful)
*/
static RDM_RETCODE modifyDataWithUpdates (
RDM_DB db, /*< [in] RDM db handle with the database open */
string mode, /*< The durability mode to use. Can be "durable" or "consistent" */
RAND &rand /*< [in] The pseudo random number generator to use */
)
{
RDM_CURSOR cursor = NULL;
RDM_TABLE_ID tables_to_lock[] = {TABLE_SAMPLE};
RDM_TRANS trans;
SW swe ("Update data (" + mode + ") - End");
SW swi ("Update data (" + mode + ") - Update 1 row");
SW sws ("Update data (" + mode + ") - Start Update");
RDM_RETCODE rc = rdm_dbSetOption (db, "durability", mode.c_str ());
for (int i = 0; rc == sOKAY && i < 1; i++)
{
sws.start ();
rc = rdm_dbStartUpdate (db, tables_to_lock, RDM_LEN (tables_to_lock), NULL, 0, &trans);
if (rc == sOKAY)
{
rc = rdm_dbGetRows (db, TABLE_SAMPLE, &cursor);
sws.stop ();
for (int i = 0; rc == sOKAY && i < rows_per_transaction; i++)
{
SAMPLE sample;
genData (sample, hash, rand);
sample.c3 = sample.c3 / 1000000;
swi.start ();
rc = updateRow (cursor, &sample, hash);
swi.stop ();
}
if (rc == sOKAY)
{
swe.start ();
rc = rdm_transEnd (trans);
swe.stop ();
}
else
{
(void) rdm_transEndRollback (trans);
}
}
}
rdm_cursorFree (cursor);
return rc;
}
/* \brief Context for the thread's entry point
*
* The thread is passed an instance of this to its entry point.
*/
typedef struct
{
RDM_TFS tfs; /*< The TFS handle to use for allocating a DB handle */
RDM_BOOL_T snapshot; /*< Use a snapshot for the reads */
} THREAD_CTX;
/* \brief Read the data from a different thread
*
* Read some of the data and report performance numbers. This is the
* thread entry point for this operation.
*
* @return Returns an \c RDM_RETCODE code (\b sOKAY if successful)
*/
static RDM_RETCODE EXTERNAL_FCN readData_thread (void *ctx)
{
THREAD_CTX *threadCtx = (THREAD_CTX *) ctx;
RDM_DB db;
RDM_RETCODE rc = rdm_tfsAllocDatabase (threadCtx->tfs, &db);
RAND randMyClone;
if (rc == sOKAY)
{
rc = rdm_dbSetOption (db, "cache_size", "500");
if (rc == sOKAY)
{
rc = rdm_dbOpen (db, "cpp50", RDM_OPEN_READONLY);
}
if (rc == sOKAY)
{
rc = readData (db, threadCtx->snapshot, randMyClone);
}
if (rc == sOKAY)
{
rc = rdm_dbFree (db);
}
else
{
}
}
return rc;
}
/* \brief Modify data and lookups in parallel
*
* Update data while at the same time lookup some data from a
* different thread. Performance number are reported for each of its
* operations.
*
* @return Returns an \c RDM_RETCODE code (\b sOKAY if successful)
*/
static RDM_RETCODE modifyDataAndLookupsInParallel (
RDM_TFS tfs, /*< [in] The TFS handle to use for allocating a DB handle */
RDM_DB db, /*< [in] RDM db handle with the database open */
string mode, /*< The durability mode to use. Can be "durable" or "consistent" */
RDM_BOOL_T snapshot, /*< [in] Use a snapshot for the reads */
RAND &rand, /*< [in] The pseudo random number generator to use */
RAND &randClone /*< [in] A clone of the original pseudo random number generator */
)
{
psp_thread_t thread;
THREAD_CTX threadCtx = {tfs, snapshot};
if (snapshot)
{
cout << "Updates and reads in parallel with snapshot:" << endl;
}
else
{
cout << "Updates and reads in parallel with read locks:" << endl;
}
rc = psp_threadBegin (&thread, readData_thread, 0, &threadCtx, 0, "readThread");
if (rc == sOKAY)
{
rc = modifyDataWithInserts (db, mode, rand);
if (rc == sOKAY)
{
rc = modifyDataWithDeletes (db, mode, randClone);
}
if (rc == sOKAY)
{
rc = psp_threadJoin (&thread);
}
else
{
psp_threadJoin (&thread);
}
}
return rc;
}
/* \brief Open and delete rows
*
* Open or create database and delete all rows from the database
*
* @return Returns an \c RDM_RETCODE code (\b sOKAY if successful)
*/
static RDM_RETCODE openDatabase (
RDM_DB db /*< [in] RDM db handle with the database open */
)
{
RDM_RETCODE rc = rdm_dbSetOption (db, "cache_size", "500");
if (rc == sOKAY)
{
rc = rdm_dbSetOption (db, "durability", "durable");
}
if (rc == sOKAY)
{
rc = rdm_dbSetOption (db, "pack_file_size", "40M");
}
if (rc == sOKAY)
{
rc = rdm_dbSetOption (db, "db_size", "300M");
}
if (rc == sOKAY)
{
rc = rdm_dbSetOption (db, "vacuum_percentage", "30");
}
if (rc == sOKAY)
{
rc = rdm_dbSetOption (db, "timeout", "-1");
}
if (rc == sOKAY)
{
rc = rdm_dbSetCatalog (db, cpp50_cat);
}
if (rc == sOKAY)
{
rc = rdm_dbOpen (db, "cpp50", RDM_OPEN_SHARED);
}
return rc;
}
/* \brief Cleanup
*
* Cleanup by dropping the database.
*/
static void cleanup (RDM_TFS tfs /*< [in] The TFS handle to use for dropping the database */
)
{
rdm_tfsDropDatabase (tfs, "cpp50");
}
/* \brief Do the DB operations
*
* This function sets up a database handle, calls openDatabase()
* to set up the database to use for this example, calls variouse
* other functions to do the main part of this performance example.
*
* @return Returns an \c RDM_RETCODE code (\b sOKAY if successful)
*/
static RDM_RETCODE doDBOperations (
RDM_TFS tfs /*< [in] The TFS handle to use for allocating a DB handle */
)
{
RDM_DB db;
cleanup (tfs);
if (rc == sOKAY)
{
const char *consistent = "consistent";
const char *durable = "durable";
RAND rand;
RAND randClone;
cout << "All operations from a single thread:" << endl;
rc = openDatabase (db);
if (rc == sOKAY)
{
rc = insertAllData (db, consistent, rand);
}
if (rc == sOKAY)
{
rc = readData (db, RDM_FALSE, randClone);
}
if (rc == sOKAY)
{
rc = readData9010 (db, RDM_FALSE, rand, randClone);
}
if (rc == sOKAY)
{
rc = modifyDataWithInserts (db, consistent, rand);
}
if (rc == sOKAY)
{
rc = modifyDataWithDeletes (db, consistent, randClone);
}
if (rc == sOKAY)
{
rc = modifyDataWithUpdates (db, consistent, randClone);
}
if (rc == sOKAY)
{
rc = modifyDataWithInserts (db, durable, rand);
}
if (rc == sOKAY)
{
rc = modifyDataWithDeletes (db, durable, randClone);
}
if (rc == sOKAY)
{
rc = modifyDataWithUpdates (db, durable, randClone);
}
if (rc == sOKAY)
{
rc = modifyDataAndLookupsInParallel (tfs, db, consistent, RDM_FALSE, rand, randClone);
}
if (rc == sOKAY)
{
rc = modifyDataAndLookupsInParallel (tfs, db, consistent, RDM_TRUE, rand, randClone);
}
if (rc == sOKAY)
{
rc = rdm_dbFree (db);
}
else
{
}
}
return rc;
}
/* Setup TFS and do the DB operations
*
* This function sets up the Transactional File Server (TFS) and
* calls doDBOperations() to do the database operations.
*
* @return Returns an \c RDM_RETCODE code (\b sOKAY if successful)
*/
static RDM_RETCODE setupTFSAndDoDBOperations (void)
{
#define MEGA_SIZE 0X100000
#define MEM_SIZE (35 * MEGA_SIZE)
RDM_ALIGNAS(0X1000) static char buf[MEM_SIZE];
RDM_TFS tfs;
RDM_RETCODE rc = rdm_rdmAllocTFSWithMemBuf (buf, sizeof(buf), &tfs);
if (rc == sOKAY)
{
rc = rdm_tfsInitialize (tfs);
if (rc == sOKAY)
{
rc = doDBOperations (tfs);
}
size_t maxMemUsage;
rdm_tfsGetMemUsage (tfs, NULL, &maxMemUsage);
cout << fixed << setprecision(1);
cout << "Maximum memory usage: " << (double) maxMemUsage / MEGA_SIZE + 1 << "M" << endl;
}
return rc;
}
/* \brief Parse the command line
*
* Parse the command line and set the global variables rows_per_transaction and
* total_number_of_rows
*/
RDM_RETCODE parseCommandLine (int argc, const char *const *argv)
{
const RDM_CMDLINE_OPT opts[] = {
{"t", "trans-size", "i=#", "Number of rows to insert/update/delete in each transaction"},
{"r", "rows", "i=#", "Total number of rows to insert initially. This number will be rounded up to the transaction size and at least 10 times the transaction size"},
{NULL, NULL, NULL, NULL}
};
RDM_RETCODE rc = rdm_cmdlineInit (&cmd, argc, argv, "A simple performance test.", opts);
if (rc == sOKAY)
{
const char *optarg;
char opt;
total_number_of_rows = 100000;
rows_per_transaction = 1000;
while ((opt = rdm_cmdlineNextShortOption (&cmd, &optarg)) != '\0')
{
switch (opt)
{
case 't':
rows_per_transaction = atoi (optarg);
break;
case 'r':
total_number_of_rows = atoi (optarg);
break;
}
}
if (total_number_of_rows < rows_per_transaction * 10)
{
total_number_of_rows = rows_per_transaction * 10;
}
}
return rc;
}
/* \brief Main function for a simple performance example
*
* This function does the command line parsing and calls
* setupTFSAndDoDBOperations() to do the rest of this example.
*
* @return Returns 0 on success and 1 on failure.
*/
int main_cpp50 (int argc, const char *const *argv)
{
RDM_RETCODE rc = parseCommandLine (argc, argv);
if (rc == sOKAY)
{
rc = setupTFSAndDoDBOperations ();
}
return rc == sOKAY ? 0 : 1;
}
uint64_t rdm_hash(uint8_t *values, uint32_t size)
Definition: hash.h:31
Header for command line parsing API.
RDM_EXPORT RDM_RETCODE psp_threadBegin(PSP_THREAD_PTR_T pThread, PSP_THREAD_FCN fcn, uint32_t stacksize, void *arg, int32_t priority, const char *name)
Definition: 50_perf_cpp/rand.h:29
RDM_RETCODE rdm_transEnd(RDM_TRANS trans)
End a transactional operation.
const char * rdm_retcodeGetName(RDM_RETCODE retcode)
Get the mnemonic name for an error or status code.
RDM_RETCODE rdm_cursorMoveToNext(RDM_CURSOR cursor)
Position a cursor to the next row in the collection.
Header for the RDM Cursor APIs.
RDM_RETCODE rdm_cursorMoveToPosition(RDM_CURSOR cursor1, RDM_CURSOR cursor2)
Position a cursor based on the currow row of another cursor.
RDM_RETCODE rdm_rdmAllocTFSWithMemBuf(void *pMemBuf, size_t memSize, RDM_TFS *phTFS)
Allocate a TFS handle with an associated memory buffer.
RDM_RETCODE rdm_tfsGetMemUsage(RDM_TFS tfs, size_t *curr_usage, size_t *max_usage)
Get memory usage associated with the TFS.
@ RDM_FALSE
Definition: psptypes.h:59
RDM_RETCODE rdm_transEndRollback(RDM_TRANS trans)
End a transactional operation with a rollback.
void start(void)
Definition: 50_perf_cpp/sw.h:60
struct RDM_CURSOR_S * RDM_CURSOR
Definition: rdmtypes.h:306
RDM_RETCODE rdm_dbStartRead(RDM_DB db, const RDM_TABLE_ID *tableIds, uint32_t numTableIds, RDM_TRANS *pTrans)
Get read locks.
RDM_RETCODE rdm_cursorReadRow(RDM_CURSOR cursor, void *colValues, size_t bytesIn, size_t *bytesOut)
Read all columns from a row.
RDM_RETCODE rdm_dbReinsertRow(RDM_DB db, RDM_TABLE_ID tableId, RDM_ROWID_T rowId, const void *colValues, size_t bytesIn, RDM_CURSOR *pCursor)
Insert a new row into a table.
void stop(void)
Definition: 50_perf_cpp/sw.h:72
The buffer used by the command line parser to hold state information.
Definition: rdmcmdlinetypes.h:85
RDM_RETCODE rdm_cursorDeleteRow(RDM_CURSOR cursor)
Delete a row from a table.
@ eNOSPACE
Definition: rdmretcodetypes.h:133
RDM_RETCODE rdm_dbSetCatalog(RDM_DB db, const char *catalog)
Associate a catalog with an allocated database.
uint32_t draw(uint32_t max)
Definition: 50_perf_cpp/rand.h:55
@ sDUPLICATE
Definition: rdmretcodetypes.h:50
@ sNOTFOUND
Definition: rdmretcodetypes.h:49
uint32_t RDM_TABLE_ID
Definition: rdmtypes.h:27
RDM_RETCODE rdm_dbFree(RDM_DB db)
Free a database handle.
RDM_RETCODE rdm_dbOpen(RDM_DB db, const char *dbNameSpec, RDM_OPEN_MODE mode)
Open an existing RDM database using the specified database handle.
#define print_error(rc)
Definition: example_fcns.h:34
@ sOKAY
Definition: rdmretcodetypes.h:96
@ RDM_TRUE
Definition: psptypes.h:60
struct RDM_TRANS_S * RDM_TRANS
Definition: rdmtypes.h:307
Generic usage function option record.
Definition: rdmcmdlinetypes.h:32
RDM_RETCODE rdm_cursorFree(RDM_CURSOR cursor)
Free an RDM_CURSOR.
RDM_RETCODE rdm_dbSetOption(RDM_DB db, const char *keyword, const char *strValue)
Set a single RDM option from a string.
@ RDM_OPEN_READONLY
Definition: rdmtypes.h:255
RDM_RETCODE rdm_tfsAllocDatabase(RDM_TFS tfs, RDM_DB *pDb)
Allocate memory for a new RDM db.
Header for the Transactional File Server (TFS) API.
#define RDM_STARTUP_EXAMPLE(name)
Definition: rdmstartuptypes.h:73
RDM_RETCODE rdm_dbStartSnapshot(RDM_DB db, const RDM_TABLE_ID *tableIds, uint32_t numTableIds, RDM_TRANS *pTrans)
Start a snapshot.
RDM_EXPORT RDM_RETCODE psp_threadJoin(PSP_THREAD_PTR_T)
RDM_RETCODE rdm_dbGetRows(RDM_DB db, RDM_TABLE_ID tableId, RDM_CURSOR *pCursor)
Associate an RDM_CURSOR with rows based on a table id.
const char * rdm_retcodeGetDescription(RDM_RETCODE retcode)
Invoke RDM error handler.
RDM_BOOL_T
Definition: psptypes.h:58
@ RDM_OPEN_SHARED
Definition: rdmtypes.h:253
Definition: 50_perf_cpp/sw.h:35
RDM_RETCODE rdm_tfsDropDatabase(RDM_TFS tfs, const char *dbNameSpec)
Drop the specified database.
uint64_t RDM_ROWID_T
Definition: rdmrowidtypes.h:21
struct RDM_TFS_S * RDM_TFS
RDM TFS Handle.
Definition: rdmtfstypes.h:21
#define RDM_ALIGNAS(align)
Definition: psptypes.h:153
RDM_RETCODE rdm_cmdlineInit(RDM_CMDLINE *cmd, int32_t argc, const char *const argv[], const char *description, const RDM_CMDLINE_OPT *opts)
Initialize an RDM_CMDLINE buffer and validate the command line.
struct RDM_DB_S * RDM_DB
Definition: rdmtypes.h:305
RDM_RETCODE rdm_cursorUpdateRow(RDM_CURSOR cursor, const void *colValues, size_t bytesIn)
Update all columns in a row.
RDM_RETCODE rdm_tfsFree(RDM_TFS hTFS)
Terminate a TFS service.
Header for the RDM Database APIs.
void print_errorEx(int rc, const char *file, int line)
char rdm_cmdlineNextShortOption(RDM_CMDLINE *cmd, const char **arg)
Get next option or argument.
RDM_RETCODE rdm_dbStartUpdate(RDM_DB db, const RDM_TABLE_ID *writeTableIds, uint32_t numWriteTableIds, const RDM_TABLE_ID *readTableIds, uint32_t numReadTableIds, RDM_TRANS *pTrans)
Get write locks.
#define RDM_LEN(x)
Definition: psptypes.h:78
RDM_RETCODE rdm_cursorMoveToRowId(RDM_CURSOR cursor, RDM_ROWID_T rowid)
Position a cursor based on a rowid.
@ sENDOFCURSOR
Definition: rdmretcodetypes.h:59
Internal RDM Startup API used by startup macros.
RDM_RETCODE
RDM status and error return codes.
Definition: rdmretcodetypes.h:44
RDM_RETCODE rdm_tfsInitialize(RDM_TFS tfs)
Initialize a RDM_TFS instance.
RDM_RETCODE rdm_cursorGetRowId(RDM_CURSOR cursor, RDM_ROWID_T *rowid)
Get the rowid for the current row of the cursor.