678
683
return (cp->name);
687
* Test driver with support for interleaved access. First, enter a number of
688
* requests to look up, update or delete a sequence of cache entries, then
689
* interleave those sequences with the "run" command.
692
#include <msg_vstream.h>
693
#include <vstring_vstream.h>
695
#include <stringops.h>
698
#define USAGE "\n\tTo manage settings:" \
699
"\n\tverbose <level> (verbosity level)" \
700
"\n\telapsed <level> (0=don't show elapsed time)" \
701
"\n\tlmdb_map_size <limit> (initial LMDB size limit)" \
702
"\n\tcache <type>:<name> (switch to named database)" \
703
"\n\tstatus (show map size, cache, pending requests)" \
704
"\n\n\tTo manage pending requests:" \
705
"\n\treset (discard pending requests)" \
706
"\n\trun (execute pending requests in interleaved order)" \
707
"\n\n\tTo add a pending request:" \
708
"\n\tquery <key-suffix> <count> (negative to reverse order)" \
709
"\n\tupdate <key-suffix> <count> (negative to reverse order)" \
710
"\n\tdelete <key-suffix> <count> (negative to reverse order)" \
711
"\n\tpurge <key-suffix>" \
712
"\n\tcount <key-suffix>"
715
* For realism, open the cache with the same flags as postscreen(8) and
718
#define DICT_CACHE_OPEN_FLAGS (DICT_FLAG_DUP_REPLACE | DICT_FLAG_SYNC_UPDATE | \
722
* Storage for one request to access a sequence of cache entries.
724
typedef struct DICT_CACHE_SREQ {
725
int flags; /* per-request: reverse, purge */
726
char *cmd; /* command for status report */
727
void (*action) (struct DICT_CACHE_SREQ *, DICT_CACHE *, VSTRING *);
728
char *suffix; /* key suffix */
729
int done; /* progress indicator */
730
int todo; /* number of entries to process */
731
int first_next; /* first/next */
734
#define DICT_CACHE_SREQ_FLAG_PURGE (1<<1) /* purge instead of count */
735
#define DICT_CACHE_SREQ_FLAG_REVERSE (1<<2) /* reverse instead of forward */
737
#define DICT_CACHE_SREQ_LIMIT 10
740
* All test requests combined.
742
typedef struct DICT_CACHE_TEST {
743
int flags; /* exclusion flags */
744
int size; /* allocated slots */
745
int used; /* used slots */
746
DICT_CACHE_SREQ job_list[1]; /* actually, a bunch */
749
#define DICT_CACHE_TEST_FLAG_ITER (1<<0) /* count or purge */
751
#define STR(x) vstring_str(x)
753
int show_elapsed = 1; /* show elapsed time */
756
extern size_t dict_lmdb_map_size; /* LMDB-specific */
760
/* usage - command-line usage message */
762
static NORETURN usage(const char *progname)
764
msg_fatal("usage: %s (no argument)", progname);
767
/* make_tagged_key - make tagged search key */
769
static void make_tagged_key(VSTRING *bp, DICT_CACHE_SREQ *cp)
772
msg_panic("make_tagged_key: bad done count: %d", cp->done);
774
msg_panic("make_tagged_key: bad todo count: %d", cp->todo);
775
vstring_sprintf(bp, "%d-%s",
776
(cp->flags & DICT_CACHE_SREQ_FLAG_REVERSE) ?
777
cp->todo - cp->done - 1 : cp->done, cp->suffix);
780
/* create_requests - create request list */
782
static DICT_CACHE_TEST *create_requests(int count)
787
tp = (DICT_CACHE_TEST *) mymalloc(sizeof(DICT_CACHE_TEST) +
788
(count - 1) *sizeof(DICT_CACHE_SREQ));
792
for (cp = tp->job_list; cp < tp->job_list + count; cp++) {
798
cp->first_next = DICT_SEQ_FUN_FIRST;
803
/* reset_requests - reset request list */
805
static void reset_requests(DICT_CACHE_TEST *tp)
811
for (cp = tp->job_list; cp < tp->job_list + tp->size; cp++) {
823
cp->first_next = DICT_SEQ_FUN_FIRST;
827
/* free_requests - destroy request list */
829
static void free_requests(DICT_CACHE_TEST *tp)
835
/* run_requests - execute pending requests in interleaved order */
837
static void run_requests(DICT_CACHE_TEST *tp, DICT_CACHE *dp, VSTRING *bp)
841
struct timeval start;
842
struct timeval finish;
843
struct timeval elapsed;
846
msg_warn("no cache");
849
GETTIMEOFDAY(&start);
852
for (cp = tp->job_list; cp < tp->job_list + tp->used; cp++) {
853
if (cp->done < cp->todo) {
855
cp->action(cp, dp, bp);
859
GETTIMEOFDAY(&finish);
860
timersub(&finish, &start, &elapsed);
862
vstream_printf("Elapsed: %g\n",
863
elapsed.tv_sec + elapsed.tv_usec / 1000000.0);
868
/* show_status - show settings and pending requests */
870
static void show_status(DICT_CACHE_TEST *tp, DICT_CACHE *dp)
875
vstream_printf("lmdb_map_size\t%ld\n", (long) dict_lmdb_map_size);
877
vstream_printf("cache\t%s\n", dp ? dp->name : "(none)");
880
vstream_printf("No pending requests\n");
882
vstream_printf("%s\t%s\t%s\t%s\t%s\t%s\n",
883
"cmd", "dir", "suffix", "count", "done", "first/next");
885
for (cp = tp->job_list; cp < tp->job_list + tp->used; cp++)
887
vstream_printf("%s\t%s\t%s\t%d\t%d\t%d\n",
889
(cp->flags & DICT_CACHE_SREQ_FLAG_REVERSE) ?
890
"reverse" : "forward",
891
cp->suffix ? cp->suffix : "(null)", cp->todo,
892
cp->done, cp->first_next);
895
/* query_action - lookup cache entry */
897
static void query_action(DICT_CACHE_SREQ *cp, DICT_CACHE *dp, VSTRING *bp)
901
make_tagged_key(bp, cp);
902
if ((lookup = dict_cache_lookup(dp, STR(bp))) == 0) {
904
msg_warn("query_action: query failed: %s: %m", STR(bp));
906
msg_warn("query_action: query failed: %s", STR(bp));
907
} else if (strcmp(STR(bp), lookup) != 0) {
908
msg_warn("lookup result \"%s\" differs from key \"%s\"",
914
/* update_action - update cache entry */
916
static void update_action(DICT_CACHE_SREQ *cp, DICT_CACHE *dp, VSTRING *bp)
918
make_tagged_key(bp, cp);
919
if (dict_cache_update(dp, STR(bp), STR(bp)) != 0) {
921
msg_warn("update_action: update failed: %s: %m", STR(bp));
923
msg_warn("update_action: update failed: %s", STR(bp));
928
/* delete_action - delete cache entry */
930
static void delete_action(DICT_CACHE_SREQ *cp, DICT_CACHE *dp, VSTRING *bp)
932
make_tagged_key(bp, cp);
933
if (dict_cache_delete(dp, STR(bp)) != 0) {
935
msg_warn("delete_action: delete failed: %s: %m", STR(bp));
937
msg_warn("delete_action: delete failed: %s", STR(bp));
942
/* iter_action - iterate over cache and act on entries with given suffix */
944
static void iter_action(DICT_CACHE_SREQ *cp, DICT_CACHE *dp, VSTRING *bp)
946
const char *cache_key;
947
const char *cache_val;
951
if (dict_cache_sequence(dp, cp->first_next, &cache_key, &cache_val) == 0) {
952
if (strcmp(cache_key, cache_val) != 0)
953
msg_warn("value \"%s\" differs from key \"%s\"",
954
cache_val, cache_key);
955
suffix = cache_key + strspn(cache_key, "0123456789");
956
if (suffix[0] == '-' && strcmp(suffix + 1, cp->suffix) == 0) {
958
cp->todo = cp->done + 1; /* XXX */
959
if ((cp->flags & DICT_CACHE_SREQ_FLAG_PURGE)
960
&& dict_cache_delete(dp, cache_key) != 0) {
962
msg_warn("purge_action: delete failed: %s: %m", STR(bp));
964
msg_warn("purge_action: delete failed: %s", STR(bp));
967
cp->first_next = DICT_SEQ_FUN_NEXT;
969
what = (cp->flags & DICT_CACHE_SREQ_FLAG_PURGE) ? "purge" : "count";
971
msg_warn("%s error after %d: %m", what, cp->done);
973
vstream_printf("suffix=%s %s=%d\n", cp->suffix, what, cp->done);
979
* Table-driven support.
981
typedef struct DICT_CACHE_SREQ_INFO {
984
void (*action) (DICT_CACHE_SREQ *, DICT_CACHE *, VSTRING *);
987
} DICT_CACHE_SREQ_INFO;
989
static DICT_CACHE_SREQ_INFO req_info[] = {
990
{"query", 3, query_action},
991
{"update", 3, update_action},
992
{"delete", 3, delete_action},
993
{"count", 2, iter_action, DICT_CACHE_TEST_FLAG_ITER},
994
{"purge", 2, iter_action, DICT_CACHE_TEST_FLAG_ITER, DICT_CACHE_SREQ_FLAG_PURGE},
998
/* add_request - add a request to the list */
1000
static void add_request(DICT_CACHE_TEST *tp, ARGV *argv)
1002
DICT_CACHE_SREQ_INFO *rp;
1003
DICT_CACHE_SREQ *cp;
1006
char *cmd = argv->argv[0];
1007
char *suffix = (argv->argc > 1 ? argv->argv[1] : 0);
1008
char *todo = (argv->argc > 2 ? argv->argv[2] : "1"); /* XXX */
1010
if (tp->used >= tp->size) {
1011
msg_warn("%s: request list is full", cmd);
1014
for (rp = req_info; /* See below */ ; rp++) {
1015
if (rp->name == 0) {
1016
vstream_printf("usage: %s\n", USAGE);
1019
if (strcmp(rp->name, argv->argv[0]) == 0
1020
&& rp->argc == argv->argc)
1023
req_flags = rp->req_flags;
1024
if (todo[0] == '-') {
1025
req_flags |= DICT_CACHE_SREQ_FLAG_REVERSE;
1028
if (!alldig(todo) || (count = atoi(todo)) == 0) {
1029
msg_warn("%s: bad count: %s", cmd, todo);
1032
if (tp->flags & rp->test_flags) {
1033
msg_warn("%s: command conflicts with other command", cmd);
1036
tp->flags |= rp->test_flags;
1037
cp = tp->job_list + tp->used;
1038
cp->cmd = mystrdup(cmd);
1039
cp->action = rp->action;
1041
cp->suffix = mystrdup(suffix);
1043
cp->flags = req_flags;
1048
/* main - main program */
1050
int main(int argc, char **argv)
1052
DICT_CACHE_TEST *test_job;
1053
VSTRING *inbuf = vstring_alloc(100);
1056
DICT_CACHE *cache = 0;
1059
msg_vstream_init(argv[0], VSTREAM_ERR);
1064
test_job = create_requests(DICT_CACHE_SREQ_LIMIT);
1066
stdin_is_tty = isatty(0);
1070
vstream_printf("> ");
1071
vstream_fflush(VSTREAM_OUT);
1073
if (vstring_fgets_nonl(inbuf, VSTREAM_IN) == 0)
1075
bufp = vstring_str(inbuf);
1076
if (!stdin_is_tty) {
1077
vstream_printf("> %s\n", bufp);
1078
vstream_fflush(VSTREAM_OUT);
1082
args = argv_split(bufp, DELIMS);
1084
vstream_printf("usage: %s\n", USAGE);
1085
vstream_fflush(VSTREAM_OUT);
1088
if (strcmp(args->argv[0], "verbose") == 0 && args->argc == 2) {
1089
msg_verbose = atoi(args->argv[1]);
1090
} else if (strcmp(args->argv[0], "elapsed") == 0 && args->argc == 2) {
1091
show_elapsed = atoi(args->argv[1]);
1093
} else if (strcmp(args->argv[0], "lmdb_map_size") == 0 && args->argc == 2) {
1094
dict_lmdb_map_size = atol(args->argv[1]);
1096
} else if (strcmp(args->argv[0], "cache") == 0 && args->argc == 2) {
1098
dict_cache_close(cache);
1099
cache = dict_cache_open(args->argv[1], O_CREAT | O_RDWR,
1100
DICT_CACHE_OPEN_FLAGS);
1101
} else if (strcmp(args->argv[0], "reset") == 0 && args->argc == 1) {
1102
reset_requests(test_job);
1103
} else if (strcmp(args->argv[0], "run") == 0 && args->argc == 1) {
1104
run_requests(test_job, cache, inbuf);
1105
} else if (strcmp(args->argv[0], "status") == 0 && args->argc == 1) {
1106
show_status(test_job, cache);
1108
add_request(test_job, args);
1110
vstream_fflush(VSTREAM_OUT);
1114
vstring_free(inbuf);
1115
free_requests(test_job);
1117
dict_cache_close(cache);