packages feed

mariadb-haskell-0.1.0.0: validation/connector_c.c

/* Matching text-protocol control using the installed MariaDB Connector/C.
 * Same SQL, ASCII payload, numeric parsing, checksums, warmups and transactions
 * as Validation.Benchmark. Haskell-only binary/TLS/micro workloads are separate.
 */
#include <mysql.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <time.h>
#include <sys/resource.h>

static MYSQL *db;
static const char *name;
static int count;
static uint64_t now_ns(void) {
    struct timespec t;
    clock_gettime(CLOCK_MONOTONIC, &t);
    return (uint64_t)t.tv_sec * 1000000000ULL + t.tv_nsec;
}
static uint64_t cpu_ns(void) {
    struct rusage r;
    getrusage(RUSAGE_SELF, &r);
    return ((uint64_t)r.ru_utime.tv_sec+r.ru_stime.tv_sec)*1000000000ULL
         + ((uint64_t)r.ru_utime.tv_usec+r.ru_stime.tv_usec)*1000ULL;
}
static void die(void) { fprintf(stderr,"Connector/C: %s\n",mysql_error(db)); exit(1); }
static void execute(const char *q) {
    if (mysql_real_query(db,q,strlen(q))) die();
    int next;
    do {
        MYSQL_RES *result=mysql_store_result(db);
        if (result) mysql_free_result(result);
        else if (mysql_field_count(db)) die();
        next=mysql_next_result(db);
        if (next>0) die();
    } while (next==0);
}
static uint64_t fetch(const char *q) {
    if (mysql_real_query(db,q,strlen(q))) die();
    MYSQL_RES *result=mysql_use_result(db);
    if (!result) die();
    MYSQL_FIELD *fields=mysql_fetch_fields(result);
    unsigned n=mysql_num_fields(result);
    MYSQL_ROW row;
    uint64_t checksum=0;
    while ((row=mysql_fetch_row(result))) {
        unsigned long *lengths=mysql_fetch_lengths(result);
        for (unsigned i=0;i<n;i++) if (row[i]) {
            switch (fields[i].type) {
                case MYSQL_TYPE_TINY: case MYSQL_TYPE_SHORT:
                case MYSQL_TYPE_LONG: case MYSQL_TYPE_LONGLONG:
                case MYSQL_TYPE_INT24: case MYSQL_TYPE_DECIMAL:
                case MYSQL_TYPE_NEWDECIMAL:
                    checksum+=(uint64_t)strtoll(row[i],NULL,10); break;
                default: checksum+=lengths[i]; break;
            }
        }
    }
    if (mysql_errno(db)) die();
    mysql_free_result(result);
    return checksum;
}
static uint64_t action(void) {
    if (!strcmp(name,"latency.text")) return fetch("SELECT 1");
    if (!strcmp(name,"read.text.narrow")) return fetch("SELECT id,n,payload FROM mb_bench_data ORDER BY id");
    if (!strcmp(name,"read.text.wide")) {
        char q[2048]="SELECT id,n";
        for (int i=0;i<30;i++) strcat(q,",payload");
        strcat(q," FROM mb_bench_data ORDER BY id");
        return fetch(q);
    }
    if (!strcmp(name,"blob.text")) return fetch("SELECT CAST(REPEAT('x',1048576) AS BINARY)");
    execute("TRUNCATE TABLE mb_bench_write");
    execute("BEGIN");
    if (!strcmp(name,"write.execute-many")) {
        size_t capacity=(size_t)count*80+1;
        char *q=calloc(capacity,1);
        if (!q) exit(2);
        size_t offset=0;
        for (int i=1;i<=count;i++) offset+=(size_t)snprintf(q+offset,capacity-offset,
            "%sINSERT INTO mb_bench_write(n) VALUES(%d)",i==1?"":";",i);
        execute(q); free(q);
    } else {
        char q[128];
        for (int i=1;i<=count;i++) {
            snprintf(q,sizeof(q),"INSERT INTO mb_bench_write(n) VALUES(%d)",i);
            execute(q);
        }
    }
    execute("COMMIT");
    if (fetch("SELECT COUNT(*) FROM mb_bench_write")!=(uint64_t)count) {
        fprintf(stderr,"inserted row count mismatch\n"); exit(1);
    }
    return fetch("SELECT COALESCE(SUM(n),0) FROM mb_bench_write");
}
int main(int argc,char **argv) {
    if (argc==2 && !strcmp(argv[1],"--version")) { puts(mysql_get_client_info()); return 0; }
    if (argc!=3) { fprintf(stderr,"usage: connector-c BENCH ITERATIONS\n"); return 2; }
    name=argv[1]; count=atoi(argv[2]);
    if (count<=0) return 2;
    const char *host=getenv("MB_HOST"),*user=getenv("MB_USER"),*pass=getenv("MB_PASSWORD"),*database=getenv("MB_DATABASE");
    db=mysql_init(NULL);
    unsigned timeout=5;
    mysql_options(db,MYSQL_OPT_CONNECT_TIMEOUT,&timeout);
    mysql_options(db,MYSQL_SET_CHARSET_NAME,"utf8mb4");
    if (!mysql_real_connect(db,host?host:"127.0.0.1",user,pass,database,
        getenv("MB_PORT")?(unsigned)atoi(getenv("MB_PORT")):3306,NULL,CLIENT_MULTI_STATEMENTS)) die();
    if (mysql_get_ssl_cipher(db)) { fprintf(stderr,"unexpected TLS in plaintext control\n"); return 1; }
    int iterations=count,units=1;
    uint64_t expected=1;
    if (!strcmp(name,"read.text.narrow")) { units=10000; expected=201300000; }
    else if (!strcmp(name,"read.text.wide")) { units=10000; expected=238420000; }
    else if (!strcmp(name,"blob.text")) { units=1048576; expected=1048576; }
    else if (!strcmp(name,"write.text") || !strcmp(name,"write.execute-many")) {
        units=count; iterations=1; expected=(uint64_t)count*(count+1)/2;
        execute("CREATE TEMPORARY TABLE mb_bench_write(n BIGINT) ENGINE=InnoDB");
    } else if (strcmp(name,"latency.text")) { fprintf(stderr,"unsupported control workload\n"); return 3; }
    for (int i=0;i<2;i++) if (action()!=expected) { fprintf(stderr,"warm-up checksum mismatch\n"); return 1; }
    uint64_t *samples=calloc((size_t)iterations,sizeof(uint64_t));
    if (!samples) return 2;
    uint64_t checksum=0,cpu0=cpu_ns(),start=now_ns();
    for (int i=0;i<iterations;i++) {
        uint64_t t=now_ns(),v=action();
        samples[i]=now_ns()-t;
        if (v!=expected) { fprintf(stderr,"benchmark checksum mismatch\n"); return 1; }
        checksum+=v;
    }
    uint64_t elapsed=now_ns()-start,cpu=cpu_ns()-cpu0;
    printf("BENCH\t%s\t%d\t%" PRIu64 "\t-1\t-1\t-1\t%" PRIu64 "\t%" PRIu64 "\t",name,iterations*units,elapsed,cpu,checksum);
    for (int i=0;i<iterations;i++) printf("%s%" PRIu64,i?",":"",samples[i]);
    putchar('\n'); free(samples);
    mysql_close(db);
    return 0;
}