diff --git a/Makefile b/Makefile index 8d65c18b059a645b16bc8b78ab40c5da4e55835f..cad767c45e5d61337c092c18a1b8bec724427605 100644 --- a/Makefile +++ b/Makefile @@ -5,39 +5,29 @@ ECHO=/bin/echo CAT=/bin/cat -# make magick +# make magic SUB_MAKE:=$(filter _%,$(notdir $(CURDIR))) OBJDIR:=$(shell $(ECHO) $(filter _%,$(MAKECMDGOALS)) | tr ' ' '\n' | cut -d/ -f1 | sort | uniq) ifeq (,$(SUB_MAKE)) -ifneq (,$(OBJDIR)) -.SUFFIXES: -MAKEFLAGS += -rR --no-print-directory SRCDIR=$(CURDIR) VPATH=$(CURDIR) -FILTERED_MAKECMDGOALS=$(subst $@/,,$(filter $@/%,$(MAKECMDGOALS))) -MODULE=$(subst tarantool_,,$(FILTERED_MAKECMDGOALS)) -.PHONY: $(OBJDIR) -$(OBJDIR): + ifneq (,$(OBJDIR)) + .SUFFIXES: + MAKEFLAGS += -rR --no-print-directory VPATH=$(CURDIR) + FILTERED_MAKECMDGOALS=$(subst $@/,,$(filter $@/%,$(MAKECMDGOALS))) + module=$(subst tarantool_,,$(FILTERED_MAKECMDGOALS)) + .PHONY: $(OBJDIR) + $(OBJDIR): +@mkdir -p $@ - +@$(MAKE) -C $@ -f $(CURDIR)/Makefile OBJDIR=$@ module=$(MODULE) $(FILTERED_MAKECMDGOALS) - -Makefile: ; -%.mk :: ; -% :: $(OBJDIR) ; @: -else -SRCDIR:=$(CURDIR) -endif -endif - -# this global rules are always defined -ifeq (,$(module)) -all: - @echo "Valid targets are:" - @echo " _release_box/tarantool_silverbox" - @echo " _release_feeder/tarantool_feeder" - @echo " _debug_box/tarantool_silverbox" - @echo " _debug_feeder/tarantool_feeder" - @echo " clean" + +@$(MAKE) -C $@ -f $(CURDIR)/Makefile SRCDIR=$(CURDIR) OBJDIR=$@ module=$(module) $(FILTERED_MAKECMDGOALS) + + Makefile: ; + %.mk :: ; + % :: $(OBJDIR) ; @: + else + SRCDIR:=$(CURDIR) + include $(SRCDIR)/scripts/rules.mk + endif else -all: tarantool_$(module) + include $(SRCDIR)/scripts/rules.mk endif ifeq ("$(origin module)", "command line") @@ -51,104 +41,9 @@ clean: @for mod in mod/*; do $(MAKE) --no-print-directory module=`basename $$mod` clean; done endif .PHONY: TAGS -tags: +TAGS: ctags -R -e -f TAGS -# then SRCDIR is defined, build type is selected and it's time to define build rules -ifneq (,$(SRCDIR)) --include $(SRCDIR)/config.mk $(SRCDIR)/scripts/config.mk -include $(SRCDIR)/scripts/config_def.mk - -ifeq (_debug,$(OBJDIR)) - DEBUG=0 -else ifeq (_test,$(OBJDIR)) - CFLAGS += --coverage -DCOVERAGE -DNDEBUG -else ifeq (_coverage,$(OBJDIR)) - CFLAGS += --coverage -DCOVERAGE -endif - -# build dir includes going first -ifneq (,$(OBJDIR)) -CFLAGS += -I$(SRCDIR)/$(OBJDIR) -I$(SRCDIR)/$(OBJDIR)/include -endif -CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/include -LIBS += -lm - -subdirs += third_party -ifneq (,$(module)) - subdirs += mod/$(module) -endif -subdirs += cfg core -include $(foreach dir,$(subdirs),$(SRCDIR)/$(dir)/Makefile) - -tarantool_$(module): $(obj) - $(CC) $(CFLAGS) $(LDFLAGS) $(LIBS) $^ -o $@ - -ifdef I -$(info * build with $(filter -D%,$(CFLAGS))) -endif - -# makefile change will force rebuild -$(obj): $(wildcard ../*.mk) $(wildcard ../scripts/*.mk) -$(obj): $(foreach dir,$(subdirs),$(SRCDIR)/$(dir)/Makefile) -$(obj): Makefile - -dep = $(patsubst %.o,%.d,$(obj)) $(patsubst %.o,%.pd,$(obj)) --include $(dep) - -ifneq (,$(OBJDIR)) -%.o: %.c - @mkdir -p $(dir $@) - $(CC) $(CFLAGS) -MD -c $< -o $@ - @sed -n -f $(SRCDIR)/scripts/slurp.sed \ - -f $(SRCDIR)/scripts/fixdep.sed \ - -e 's!$(SRCDIR)/!!; p' \ - < $(@:.o=.d) > $(@:.o=.pd) -else -%.o: %.c - @mkdir -p $(dir $@) - $(CC) $(CFLAGS) -MD -c $< -o $@ -endif - -# code gen targets -.PRECIOUS: %.h %.c %.dot -ifeq ($(HAVE_RAGEL),1) -%.c: %.rl - @mkdir -p $(dir $@) - $(RAGEL) -G2 $< -o $@ - -%.dot: %.rl - @mkdir -p $(dir $@) - $(RAGEL) -p -V $< -o $(basename $@).dot - -%.png: %.dot - @mkdir -p $(dir $@) - $(DOT) -Tpng $< -o $(basename $@).png -endif -ifeq ($(HAVE_CONFETTI),1) -%.cfg: %.cfg_tmpl - @mkdir -p $(dir $@) - $(CONFETTI) -i $< -n tarantool_cfg -f $@ - -%.h: %.cfg_tmpl - @mkdir -p $(dir $@) - $(CONFETTI) -i $< -n tarantool_cfg -h $@ - -%.c: %.cfg_tmpl - @mkdir -p $(dir $@) - $(CONFETTI) -i $< -n tarantool_cfg -c $@ -endif - -ifeq ($(HAVE_GIT),1) -tarantool_version.h: FORCE - @echo -n "const char tarantool_version_string[] = " > $@_ - @git show HEAD | sed 's/commit \(.*\)/\"\1/;q' | tr -d \\n >> $@_ - @git diff --quiet || (echo -n ' AND'; git diff --shortstat) | tr -d \\n >> $@_ - @echo '";' >> $@_ - @diff -q $@ $@_ 2>/dev/null >/dev/null || ($(ECHO) " GEN " $(notdir $@); cp $@_ $@) -FORCE: -endif - ifeq ("$(origin V)", "command line") VERBOSE = $(V) @@ -160,5 +55,3 @@ ifeq (,$(VERBOSE)) $(eval override CONFETTI = @$(ECHO) " CNF " $$@; $(CONFETTI)) $(eval override CAT = @$(ECHO) " CAT " $$@; $(CAT)) endif - -endif diff --git a/README b/README index 276427712ef40001b6ca12bbdc231d320ab76e03..3406cf84d257205124676a34af12da1470982ef0 100644 --- a/README +++ b/README @@ -19,8 +19,8 @@ Cons: How to run: -1) compile - make _release_box/tarantool_silverbox +1) compile (note GNU make is required) + (g)make _release_box/tarantool_silverbox 2) customize config emacs cfg/tarantool_silverbox_cfg.cfg 3) initialize storage diff --git a/cfg/core_cfg.cfg_tmpl b/cfg/core_cfg.cfg_tmpl index f2383cb0c1858ee91a7b0c10d00b9fe4fc5fdcf4..b2b930d91892b63d1aa571caac9200e175402b7b 100644 --- a/cfg/core_cfg.cfg_tmpl +++ b/cfg/core_cfg.cfg_tmpl @@ -37,9 +37,6 @@ logger_nonblock=1 # delay between loop iteraions io_collect_interval=0.0 -# do not write snapshot faster then snap_io_rate_limit MBytes/sec -snap_io_rate_limit=0.0 - # size of listen backlog backlog=1024 diff --git a/cfg/tarantool_feeder_cfg.c b/cfg/tarantool_feeder_cfg.c index a57bacbdd887d931f2ac69482b9891230492dca8..4b8515cb66abaf240c2658bee87fc9c2a2166835 100644 --- a/cfg/tarantool_feeder_cfg.c +++ b/cfg/tarantool_feeder_cfg.c @@ -40,12 +40,12 @@ fill_default_tarantool_cfg(tarantool_cfg *c) { c->logger = NULL; c->logger_nonblock = 1; c->io_collect_interval = 0; - c->snap_io_rate_limit = 0; c->backlog = 1024; c->readahead = 16320; c->wal_feeder_bind_ipaddr = NULL; c->wal_feeder_bind_port = 0; c->wal_feeder_dir = NULL; + c->custom_proc_title = NULL; return 0; } @@ -85,9 +85,6 @@ static NameAtom _name__logger_nonblock[] = { static NameAtom _name__io_collect_interval[] = { { "io_collect_interval", -1, NULL } }; -static NameAtom _name__snap_io_rate_limit[] = { - { "snap_io_rate_limit", -1, NULL } -}; static NameAtom _name__backlog[] = { { "backlog", -1, NULL } }; @@ -103,6 +100,9 @@ static NameAtom _name__wal_feeder_bind_port[] = { static NameAtom _name__wal_feeder_dir[] = { { "wal_feeder_dir", -1, NULL } }; +static NameAtom _name__custom_proc_title[] = { + { "custom_proc_title", -1, NULL } +}; #define ARRAYALLOC(x,n,t) do { \ int l = 0, ar; \ @@ -244,14 +244,6 @@ acceptValue(tarantool_cfg* c, OptDef* opt, int check_rdonly) { if ( (c->io_collect_interval == 0 || c->io_collect_interval == -HUGE_VAL || c->io_collect_interval == HUGE_VAL) && errno == ERANGE) return CNF_WRONGRANGE; } - else if ( cmpNameAtoms( opt->name, _name__snap_io_rate_limit) ) { - if (opt->paramType != numberType ) - return CNF_WRONGTYPE; - errno = 0; - c->snap_io_rate_limit = strtod(opt->paramValue.numberval, NULL); - if ( (c->snap_io_rate_limit == 0 || c->snap_io_rate_limit == -HUGE_VAL || c->snap_io_rate_limit == HUGE_VAL) && errno == ERANGE) - return CNF_WRONGRANGE; - } else if ( cmpNameAtoms( opt->name, _name__backlog) ) { if (opt->paramType != numberType ) return CNF_WRONGTYPE; @@ -301,6 +293,14 @@ acceptValue(tarantool_cfg* c, OptDef* opt, int check_rdonly) { if (opt->paramValue.stringval && c->wal_feeder_dir == NULL) return CNF_NOMEMORY; } + else if ( cmpNameAtoms( opt->name, _name__custom_proc_title) ) { + if (opt->paramType != stringType ) + return CNF_WRONGTYPE; + errno = 0; + c->custom_proc_title = (opt->paramValue.stringval) ? strdup(opt->paramValue.stringval) : NULL; + if (opt->paramValue.stringval && c->custom_proc_title == NULL) + return CNF_NOMEMORY; + } else { return CNF_MISSED; } @@ -366,6 +366,10 @@ acceptCfgDef(tarantool_cfg *c, OptDef *opt, int check_rdonly, int *n_accepted, i out_warning(r, "Not enough memory to accept '%s' option", dumpOptDef(opt->name)); if (n_skipped) (*n_skipped)++; break; + case CNF_NOTSET: + out_warning(r, "Option '%s' is not set (or has a default value)", dumpOptDef(opt->name)); + if (n_skipped) (*n_skipped)++; + break; default: out_warning(r, "Unknown error for '%s' option", dumpOptDef(opt->name)); if (n_skipped) (*n_skipped)++; @@ -409,12 +413,12 @@ typedef enum IteratorState { S_name__logger, S_name__logger_nonblock, S_name__io_collect_interval, - S_name__snap_io_rate_limit, S_name__backlog, S_name__readahead, S_name__wal_feeder_bind_ipaddr, S_name__wal_feeder_bind_port, S_name__wal_feeder_dir, + S_name__custom_proc_title, _S_Finished } IteratorState; @@ -565,17 +569,6 @@ tarantool_cfg_iterator_next(tarantool_cfg_iterator_t* i, tarantool_cfg *c, char } sprintf(*v, "%g", c->io_collect_interval); snprintf(buf, PRINTBUFLEN-1, "io_collect_interval"); - i->state = S_name__snap_io_rate_limit; - return buf; - case S_name__snap_io_rate_limit: - *v = malloc(32); - if (*v == NULL) { - free(i); - out_warning(CNF_NOMEMORY, "No memory to output value"); - return NULL; - } - sprintf(*v, "%g", c->snap_io_rate_limit); - snprintf(buf, PRINTBUFLEN-1, "snap_io_rate_limit"); i->state = S_name__backlog; return buf; case S_name__backlog: @@ -629,6 +622,16 @@ tarantool_cfg_iterator_next(tarantool_cfg_iterator_t* i, tarantool_cfg *c, char return NULL; } snprintf(buf, PRINTBUFLEN-1, "wal_feeder_dir"); + i->state = S_name__custom_proc_title; + return buf; + case S_name__custom_proc_title: + *v = (c->custom_proc_title) ? strdup(c->custom_proc_title) : NULL; + if (*v == NULL && c->custom_proc_title) { + free(i); + out_warning(CNF_NOMEMORY, "No memory to output value"); + return NULL; + } + snprintf(buf, PRINTBUFLEN-1, "custom_proc_title"); i->state = _S_Finished; return buf; case _S_Finished: @@ -641,3 +644,12 @@ tarantool_cfg_iterator_next(tarantool_cfg_iterator_t* i, tarantool_cfg *c, char return NULL; } +/************** Checking of required fields **************/ +int +check_cfg_tarantool_cfg(tarantool_cfg *c) { + tarantool_cfg_iterator_t iterator, *i = &iterator; + int res = 0; + + return res; +} + diff --git a/cfg/tarantool_feeder_cfg.cfg b/cfg/tarantool_feeder_cfg.cfg index 7fbe2a5823ee627b633368ed1d2823444ca66928..f6c7690bdd38b73ad4c9aadaae7208fdeda82204 100644 --- a/cfg/tarantool_feeder_cfg.cfg +++ b/cfg/tarantool_feeder_cfg.cfg @@ -39,9 +39,6 @@ logger_nonblock = 1 # delay between loop iteraions io_collect_interval = 0 -# do not write snapshot faster then snap_io_rate_limit MBytes/sec -snap_io_rate_limit = 0 - # size of listen backlog backlog = 1024 @@ -55,3 +52,6 @@ wal_feeder_bind_port = 0 # Directory with WAL files to serve wal_feeder_dir = NULL + +# custom proc title is appended after normal +custom_proc_title = NULL diff --git a/cfg/tarantool_feeder_cfg.cfg_tmpl b/cfg/tarantool_feeder_cfg.cfg_tmpl index c34b958f35bd639dccb175a702422ec84aeac1d0..c87a3980a1a4d85110054b9be70f25c0f598a697 100644 --- a/cfg/tarantool_feeder_cfg.cfg_tmpl +++ b/cfg/tarantool_feeder_cfg.cfg_tmpl @@ -42,9 +42,6 @@ logger_nonblock=1 # delay between loop iteraions io_collect_interval=0.0 -# do not write snapshot faster then snap_io_rate_limit MBytes/sec -snap_io_rate_limit=0.0 - # size of listen backlog backlog=1024 @@ -57,3 +54,6 @@ wal_feeder_bind_port=0 # Directory with WAL files to serve wal_feeder_dir=NULL + +# custom proc title is appended after normal +custom_proc_title=NULL diff --git a/cfg/tarantool_feeder_cfg.h b/cfg/tarantool_feeder_cfg.h index 57955aed2eea86e8b01325242a7b66e314074cd6..f7b61f65ff2df127086848c9101199177bd23edf 100644 --- a/cfg/tarantool_feeder_cfg.h +++ b/cfg/tarantool_feeder_cfg.h @@ -54,9 +54,6 @@ typedef struct tarantool_cfg { /* delay between loop iteraions */ double io_collect_interval; - /* do not write snapshot faster then snap_io_rate_limit MBytes/sec */ - double snap_io_rate_limit; - /* size of listen backlog */ int32_t backlog; @@ -72,6 +69,9 @@ typedef struct tarantool_cfg { /* Directory with WAL files to serve */ char* wal_feeder_dir; + + /* custom proc title is appended after normal */ + char* custom_proc_title; } tarantool_cfg; int fill_default_tarantool_cfg(tarantool_cfg *c); @@ -79,6 +79,8 @@ void parse_cfg_file_tarantool_cfg(tarantool_cfg *c, FILE *fh, int check_rdonly, void parse_cfg_buffer_tarantool_cfg(tarantool_cfg *c, char *buffer, int check_rdonly, int *n_accepted, int *n_skipped); +int check_cfg_tarantool_cfg(tarantool_cfg *c); + typedef struct tarantool_cfg_iterator_t tarantool_cfg_iterator_t; tarantool_cfg_iterator_t* tarantool_cfg_iterator_init(); char* tarantool_cfg_iterator_next(tarantool_cfg_iterator_t* i, tarantool_cfg *c, char **v); diff --git a/cfg/tarantool_silverbox_cfg.c b/cfg/tarantool_silverbox_cfg.c index d38647366dd89ef0e0188c162bde493d84aa25fe..02db5416d2f4b4329b9e8b15d298ff0bfc47b1d6 100644 --- a/cfg/tarantool_silverbox_cfg.c +++ b/cfg/tarantool_silverbox_cfg.c @@ -40,7 +40,6 @@ fill_default_tarantool_cfg(tarantool_cfg *c) { c->logger = NULL; c->logger_nonblock = 1; c->io_collect_interval = 0; - c->snap_io_rate_limit = 0; c->backlog = 1024; c->readahead = 16320; c->snap_dir = strdup("."); @@ -55,11 +54,14 @@ fill_default_tarantool_cfg(tarantool_cfg *c) { c->memcached_namespace = 23; c->memcached_expire_per_loop = 1024; c->memcached_expire_full_sweep = 3600; + c->snap_io_rate_limit = 0; c->rows_per_wal = 500000; c->wal_fsync_delay = 0; c->wal_writer_inbox_size = 128; c->local_hot_standby = 0; c->wal_dir_rescan_delay = 0.1; + c->panic_on_snap_error = 1; + c->panic_on_wal_error = 0; c->remote_hot_standby = 0; c->wal_feeder_ipaddr = NULL; c->wal_feeder_port = 0; @@ -78,9 +80,18 @@ acceptDefault_name__namespace(tarantool_cfg_namespace *c) { static int acceptDefault_name__namespace__index(tarantool_cfg_namespace_index *c) { - c->type = strdup("NUM"); + c->type = strdup(""); + if (c->type == NULL) return CNF_NOMEMORY; + c->unique = -1; + c->key_field = NULL; + return 0; +} + +static int +acceptDefault_name__namespace__index__key_field(tarantool_cfg_namespace_index_key_field *c) { + c->fieldno = -1; + c->type = strdup(""); if (c->type == NULL) return CNF_NOMEMORY; - c->key_position = -1; return 0; } @@ -120,9 +131,6 @@ static NameAtom _name__logger_nonblock[] = { static NameAtom _name__io_collect_interval[] = { { "io_collect_interval", -1, NULL } }; -static NameAtom _name__snap_io_rate_limit[] = { - { "snap_io_rate_limit", -1, NULL } -}; static NameAtom _name__backlog[] = { { "backlog", -1, NULL } }; @@ -159,6 +167,9 @@ static NameAtom _name__memcached_expire_per_loop[] = { static NameAtom _name__memcached_expire_full_sweep[] = { { "memcached_expire_full_sweep", -1, NULL } }; +static NameAtom _name__snap_io_rate_limit[] = { + { "snap_io_rate_limit", -1, NULL } +}; static NameAtom _name__rows_per_wal[] = { { "rows_per_wal", -1, NULL } }; @@ -174,6 +185,12 @@ static NameAtom _name__local_hot_standby[] = { static NameAtom _name__wal_dir_rescan_delay[] = { { "wal_dir_rescan_delay", -1, NULL } }; +static NameAtom _name__panic_on_snap_error[] = { + { "panic_on_snap_error", -1, NULL } +}; +static NameAtom _name__panic_on_wal_error[] = { + { "panic_on_wal_error", -1, NULL } +}; static NameAtom _name__remote_hot_standby[] = { { "remote_hot_standby", -1, NULL } }; @@ -200,10 +217,22 @@ static NameAtom _name__namespace__index__type[] = { { "index", -1, _name__namespace__index__type + 2 }, { "type", -1, NULL } }; -static NameAtom _name__namespace__index__key_position[] = { - { "namespace", -1, _name__namespace__index__key_position + 1 }, - { "index", -1, _name__namespace__index__key_position + 2 }, - { "key_position", -1, NULL } +static NameAtom _name__namespace__index__unique[] = { + { "namespace", -1, _name__namespace__index__unique + 1 }, + { "index", -1, _name__namespace__index__unique + 2 }, + { "unique", -1, NULL } +}; +static NameAtom _name__namespace__index__key_field__fieldno[] = { + { "namespace", -1, _name__namespace__index__key_field__fieldno + 1 }, + { "index", -1, _name__namespace__index__key_field__fieldno + 2 }, + { "key_field", -1, _name__namespace__index__key_field__fieldno + 3 }, + { "fieldno", -1, NULL } +}; +static NameAtom _name__namespace__index__key_field__type[] = { + { "namespace", -1, _name__namespace__index__key_field__type + 1 }, + { "index", -1, _name__namespace__index__key_field__type + 2 }, + { "key_field", -1, _name__namespace__index__key_field__type + 3 }, + { "type", -1, NULL } }; #define ARRAYALLOC(x,n,t) do { \ @@ -346,14 +375,6 @@ acceptValue(tarantool_cfg* c, OptDef* opt, int check_rdonly) { if ( (c->io_collect_interval == 0 || c->io_collect_interval == -HUGE_VAL || c->io_collect_interval == HUGE_VAL) && errno == ERANGE) return CNF_WRONGRANGE; } - else if ( cmpNameAtoms( opt->name, _name__snap_io_rate_limit) ) { - if (opt->paramType != numberType ) - return CNF_WRONGTYPE; - errno = 0; - c->snap_io_rate_limit = strtod(opt->paramValue.numberval, NULL); - if ( (c->snap_io_rate_limit == 0 || c->snap_io_rate_limit == -HUGE_VAL || c->snap_io_rate_limit == HUGE_VAL) && errno == ERANGE) - return CNF_WRONGRANGE; - } else if ( cmpNameAtoms( opt->name, _name__backlog) ) { if (opt->paramType != numberType ) return CNF_WRONGTYPE; @@ -474,6 +495,14 @@ acceptValue(tarantool_cfg* c, OptDef* opt, int check_rdonly) { return CNF_WRONGRANGE; c->memcached_expire_full_sweep = i32; } + else if ( cmpNameAtoms( opt->name, _name__snap_io_rate_limit) ) { + if (opt->paramType != numberType ) + return CNF_WRONGTYPE; + errno = 0; + c->snap_io_rate_limit = strtod(opt->paramValue.numberval, NULL); + if ( (c->snap_io_rate_limit == 0 || c->snap_io_rate_limit == -HUGE_VAL || c->snap_io_rate_limit == HUGE_VAL) && errno == ERANGE) + return CNF_WRONGRANGE; + } else if ( cmpNameAtoms( opt->name, _name__rows_per_wal) ) { if (opt->paramType != numberType ) return CNF_WRONGTYPE; @@ -526,6 +555,28 @@ acceptValue(tarantool_cfg* c, OptDef* opt, int check_rdonly) { if ( (c->wal_dir_rescan_delay == 0 || c->wal_dir_rescan_delay == -HUGE_VAL || c->wal_dir_rescan_delay == HUGE_VAL) && errno == ERANGE) return CNF_WRONGRANGE; } + else if ( cmpNameAtoms( opt->name, _name__panic_on_snap_error) ) { + if (opt->paramType != numberType ) + return CNF_WRONGTYPE; + errno = 0; + long int i32 = strtol(opt->paramValue.numberval, NULL, 10); + if (i32 == 0 && errno == EINVAL) + return CNF_WRONGINT; + if ( (i32 == LONG_MIN || i32 == LONG_MAX) && errno == ERANGE) + return CNF_WRONGRANGE; + c->panic_on_snap_error = i32; + } + else if ( cmpNameAtoms( opt->name, _name__panic_on_wal_error) ) { + if (opt->paramType != numberType ) + return CNF_WRONGTYPE; + errno = 0; + long int i32 = strtol(opt->paramValue.numberval, NULL, 10); + if (i32 == 0 && errno == EINVAL) + return CNF_WRONGINT; + if ( (i32 == LONG_MIN || i32 == LONG_MAX) && errno == ERANGE) + return CNF_WRONGRANGE; + c->panic_on_wal_error = i32; + } else if ( cmpNameAtoms( opt->name, _name__remote_hot_standby) ) { if (opt->paramType != numberType ) return CNF_WRONGTYPE; @@ -602,18 +653,43 @@ acceptValue(tarantool_cfg* c, OptDef* opt, int check_rdonly) { if (opt->paramValue.stringval && c->namespace[opt->name->index]->index[opt->name->next->index]->type == NULL) return CNF_NOMEMORY; } - else if ( cmpNameAtoms( opt->name, _name__namespace__index__key_position) ) { + else if ( cmpNameAtoms( opt->name, _name__namespace__index__unique) ) { + if (opt->paramType != numberType ) + return CNF_WRONGTYPE; + ARRAYALLOC(c->namespace, opt->name->index + 1, _name__namespace); + ARRAYALLOC(c->namespace[opt->name->index]->index, opt->name->next->index + 1, _name__namespace__index); + errno = 0; + long int i32 = strtol(opt->paramValue.numberval, NULL, 10); + if (i32 == 0 && errno == EINVAL) + return CNF_WRONGINT; + if ( (i32 == LONG_MIN || i32 == LONG_MAX) && errno == ERANGE) + return CNF_WRONGRANGE; + c->namespace[opt->name->index]->index[opt->name->next->index]->unique = i32; + } + else if ( cmpNameAtoms( opt->name, _name__namespace__index__key_field__fieldno) ) { if (opt->paramType != numberType ) return CNF_WRONGTYPE; ARRAYALLOC(c->namespace, opt->name->index + 1, _name__namespace); ARRAYALLOC(c->namespace[opt->name->index]->index, opt->name->next->index + 1, _name__namespace__index); + ARRAYALLOC(c->namespace[opt->name->index]->index[opt->name->next->index]->key_field, opt->name->next->next->index + 1, _name__namespace__index__key_field); errno = 0; long int i32 = strtol(opt->paramValue.numberval, NULL, 10); if (i32 == 0 && errno == EINVAL) return CNF_WRONGINT; if ( (i32 == LONG_MIN || i32 == LONG_MAX) && errno == ERANGE) return CNF_WRONGRANGE; - c->namespace[opt->name->index]->index[opt->name->next->index]->key_position = i32; + c->namespace[opt->name->index]->index[opt->name->next->index]->key_field[opt->name->next->next->index]->fieldno = i32; + } + else if ( cmpNameAtoms( opt->name, _name__namespace__index__key_field__type) ) { + if (opt->paramType != stringType ) + return CNF_WRONGTYPE; + ARRAYALLOC(c->namespace, opt->name->index + 1, _name__namespace); + ARRAYALLOC(c->namespace[opt->name->index]->index, opt->name->next->index + 1, _name__namespace__index); + ARRAYALLOC(c->namespace[opt->name->index]->index[opt->name->next->index]->key_field, opt->name->next->next->index + 1, _name__namespace__index__key_field); + errno = 0; + c->namespace[opt->name->index]->index[opt->name->next->index]->key_field[opt->name->next->next->index]->type = (opt->paramValue.stringval) ? strdup(opt->paramValue.stringval) : NULL; + if (opt->paramValue.stringval && c->namespace[opt->name->index]->index[opt->name->next->index]->key_field[opt->name->next->next->index]->type == NULL) + return CNF_NOMEMORY; } else { return CNF_MISSED; @@ -680,6 +756,10 @@ acceptCfgDef(tarantool_cfg *c, OptDef *opt, int check_rdonly, int *n_accepted, i out_warning(r, "Not enough memory to accept '%s' option", dumpOptDef(opt->name)); if (n_skipped) (*n_skipped)++; break; + case CNF_NOTSET: + out_warning(r, "Option '%s' is not set (or has a default value)", dumpOptDef(opt->name)); + if (n_skipped) (*n_skipped)++; + break; default: out_warning(r, "Unknown error for '%s' option", dumpOptDef(opt->name)); if (n_skipped) (*n_skipped)++; @@ -723,7 +803,6 @@ typedef enum IteratorState { S_name__logger, S_name__logger_nonblock, S_name__io_collect_interval, - S_name__snap_io_rate_limit, S_name__backlog, S_name__readahead, S_name__snap_dir, @@ -736,11 +815,14 @@ typedef enum IteratorState { S_name__memcached_namespace, S_name__memcached_expire_per_loop, S_name__memcached_expire_full_sweep, + S_name__snap_io_rate_limit, S_name__rows_per_wal, S_name__wal_fsync_delay, S_name__wal_writer_inbox_size, S_name__local_hot_standby, S_name__wal_dir_rescan_delay, + S_name__panic_on_snap_error, + S_name__panic_on_wal_error, S_name__remote_hot_standby, S_name__wal_feeder_ipaddr, S_name__wal_feeder_port, @@ -750,7 +832,10 @@ typedef enum IteratorState { S_name__namespace__estimated_rows, S_name__namespace__index, S_name__namespace__index__type, - S_name__namespace__index__key_position, + S_name__namespace__index__unique, + S_name__namespace__index__key_field, + S_name__namespace__index__key_field__fieldno, + S_name__namespace__index__key_field__type, _S_Finished } IteratorState; @@ -758,6 +843,7 @@ struct tarantool_cfg_iterator_t { IteratorState state; int idx_name__namespace; int idx_name__namespace__index; + int idx_name__namespace__index__key_field; }; tarantool_cfg_iterator_t* @@ -903,17 +989,6 @@ tarantool_cfg_iterator_next(tarantool_cfg_iterator_t* i, tarantool_cfg *c, char } sprintf(*v, "%g", c->io_collect_interval); snprintf(buf, PRINTBUFLEN-1, "io_collect_interval"); - i->state = S_name__snap_io_rate_limit; - return buf; - case S_name__snap_io_rate_limit: - *v = malloc(32); - if (*v == NULL) { - free(i); - out_warning(CNF_NOMEMORY, "No memory to output value"); - return NULL; - } - sprintf(*v, "%g", c->snap_io_rate_limit); - snprintf(buf, PRINTBUFLEN-1, "snap_io_rate_limit"); i->state = S_name__backlog; return buf; case S_name__backlog: @@ -1043,6 +1118,17 @@ tarantool_cfg_iterator_next(tarantool_cfg_iterator_t* i, tarantool_cfg *c, char } sprintf(*v, "%"PRId32, c->memcached_expire_full_sweep); snprintf(buf, PRINTBUFLEN-1, "memcached_expire_full_sweep"); + i->state = S_name__snap_io_rate_limit; + return buf; + case S_name__snap_io_rate_limit: + *v = malloc(32); + if (*v == NULL) { + free(i); + out_warning(CNF_NOMEMORY, "No memory to output value"); + return NULL; + } + sprintf(*v, "%g", c->snap_io_rate_limit); + snprintf(buf, PRINTBUFLEN-1, "snap_io_rate_limit"); i->state = S_name__rows_per_wal; return buf; case S_name__rows_per_wal: @@ -1098,6 +1184,28 @@ tarantool_cfg_iterator_next(tarantool_cfg_iterator_t* i, tarantool_cfg *c, char } sprintf(*v, "%g", c->wal_dir_rescan_delay); snprintf(buf, PRINTBUFLEN-1, "wal_dir_rescan_delay"); + i->state = S_name__panic_on_snap_error; + return buf; + case S_name__panic_on_snap_error: + *v = malloc(32); + if (*v == NULL) { + free(i); + out_warning(CNF_NOMEMORY, "No memory to output value"); + return NULL; + } + sprintf(*v, "%"PRId32, c->panic_on_snap_error); + snprintf(buf, PRINTBUFLEN-1, "panic_on_snap_error"); + i->state = S_name__panic_on_wal_error; + return buf; + case S_name__panic_on_wal_error: + *v = malloc(32); + if (*v == NULL) { + free(i); + out_warning(CNF_NOMEMORY, "No memory to output value"); + return NULL; + } + sprintf(*v, "%"PRId32, c->panic_on_wal_error); + snprintf(buf, PRINTBUFLEN-1, "panic_on_wal_error"); i->state = S_name__remote_hot_standby; return buf; case S_name__remote_hot_standby: @@ -1139,7 +1247,10 @@ tarantool_cfg_iterator_next(tarantool_cfg_iterator_t* i, tarantool_cfg *c, char case S_name__namespace__estimated_rows: case S_name__namespace__index: case S_name__namespace__index__type: - case S_name__namespace__index__key_position: + case S_name__namespace__index__unique: + case S_name__namespace__index__key_field: + case S_name__namespace__index__key_field__fieldno: + case S_name__namespace__index__key_field__type: if (c->namespace && c->namespace[i->idx_name__namespace]) { switch(i->state) { case S_name__namespace: @@ -1179,7 +1290,10 @@ tarantool_cfg_iterator_next(tarantool_cfg_iterator_t* i, tarantool_cfg *c, char case S_name__namespace__index: i->state = S_name__namespace__index; case S_name__namespace__index__type: - case S_name__namespace__index__key_position: + case S_name__namespace__index__unique: + case S_name__namespace__index__key_field: + case S_name__namespace__index__key_field__fieldno: + case S_name__namespace__index__key_field__type: if (c->namespace[i->idx_name__namespace]->index && c->namespace[i->idx_name__namespace]->index[i->idx_name__namespace__index]) { switch(i->state) { case S_name__namespace__index: @@ -1191,20 +1305,58 @@ tarantool_cfg_iterator_next(tarantool_cfg_iterator_t* i, tarantool_cfg *c, char return NULL; } snprintf(buf, PRINTBUFLEN-1, "namespace[%d].index[%d].type", i->idx_name__namespace, i->idx_name__namespace__index); - i->state = S_name__namespace__index__key_position; + i->state = S_name__namespace__index__unique; return buf; - case S_name__namespace__index__key_position: + case S_name__namespace__index__unique: *v = malloc(32); if (*v == NULL) { free(i); out_warning(CNF_NOMEMORY, "No memory to output value"); return NULL; } - sprintf(*v, "%"PRId32, c->namespace[i->idx_name__namespace]->index[i->idx_name__namespace__index]->key_position); - snprintf(buf, PRINTBUFLEN-1, "namespace[%d].index[%d].key_position", i->idx_name__namespace, i->idx_name__namespace__index); - i->state = S_name__namespace__index; - i->idx_name__namespace__index++; + sprintf(*v, "%"PRId32, c->namespace[i->idx_name__namespace]->index[i->idx_name__namespace__index]->unique); + snprintf(buf, PRINTBUFLEN-1, "namespace[%d].index[%d].unique", i->idx_name__namespace, i->idx_name__namespace__index); + i->state = S_name__namespace__index__key_field; return buf; + case S_name__namespace__index__key_field: + i->state = S_name__namespace__index__key_field; + case S_name__namespace__index__key_field__fieldno: + case S_name__namespace__index__key_field__type: + if (c->namespace[i->idx_name__namespace]->index[i->idx_name__namespace__index]->key_field && c->namespace[i->idx_name__namespace]->index[i->idx_name__namespace__index]->key_field[i->idx_name__namespace__index__key_field]) { + switch(i->state) { + case S_name__namespace__index__key_field: + case S_name__namespace__index__key_field__fieldno: + *v = malloc(32); + if (*v == NULL) { + free(i); + out_warning(CNF_NOMEMORY, "No memory to output value"); + return NULL; + } + sprintf(*v, "%"PRId32, c->namespace[i->idx_name__namespace]->index[i->idx_name__namespace__index]->key_field[i->idx_name__namespace__index__key_field]->fieldno); + snprintf(buf, PRINTBUFLEN-1, "namespace[%d].index[%d].key_field[%d].fieldno", i->idx_name__namespace, i->idx_name__namespace__index, i->idx_name__namespace__index__key_field); + i->state = S_name__namespace__index__key_field__type; + return buf; + case S_name__namespace__index__key_field__type: + *v = (c->namespace[i->idx_name__namespace]->index[i->idx_name__namespace__index]->key_field[i->idx_name__namespace__index__key_field]->type) ? strdup(c->namespace[i->idx_name__namespace]->index[i->idx_name__namespace__index]->key_field[i->idx_name__namespace__index__key_field]->type) : NULL; + if (*v == NULL && c->namespace[i->idx_name__namespace]->index[i->idx_name__namespace__index]->key_field[i->idx_name__namespace__index__key_field]->type) { + free(i); + out_warning(CNF_NOMEMORY, "No memory to output value"); + return NULL; + } + snprintf(buf, PRINTBUFLEN-1, "namespace[%d].index[%d].key_field[%d].type", i->idx_name__namespace, i->idx_name__namespace__index, i->idx_name__namespace__index__key_field); + i->state = S_name__namespace__index__key_field; + i->idx_name__namespace__index__key_field++; + return buf; + default: + break; + } + } + else { + i->state = S_name__namespace__index; + i->idx_name__namespace__index++; + i->idx_name__namespace__index__key_field = 0; + goto again; + } default: break; } @@ -1213,6 +1365,7 @@ tarantool_cfg_iterator_next(tarantool_cfg_iterator_t* i, tarantool_cfg *c, char i->state = S_name__namespace; i->idx_name__namespace++; i->idx_name__namespace__index = 0; + i->idx_name__namespace__index__key_field = 0; goto again; } default: @@ -1229,3 +1382,27 @@ tarantool_cfg_iterator_next(tarantool_cfg_iterator_t* i, tarantool_cfg *c, char return NULL; } +/************** Checking of required fields **************/ +int +check_cfg_tarantool_cfg(tarantool_cfg *c) { + tarantool_cfg_iterator_t iterator, *i = &iterator; + int res = 0; + + i->idx_name__namespace = 0; + while (c->namespace && c->namespace[i->idx_name__namespace]) { + i->idx_name__namespace__index = 0; + while (c->namespace[i->idx_name__namespace]->index && c->namespace[i->idx_name__namespace]->index[i->idx_name__namespace__index]) { + i->idx_name__namespace__index__key_field = 0; + while (c->namespace[i->idx_name__namespace]->index[i->idx_name__namespace__index]->key_field && c->namespace[i->idx_name__namespace]->index[i->idx_name__namespace__index]->key_field[i->idx_name__namespace__index__key_field]) { + i->idx_name__namespace__index__key_field++; + } + + i->idx_name__namespace__index++; + } + + i->idx_name__namespace++; + } + + return res; +} + diff --git a/cfg/tarantool_silverbox_cfg.cfg b/cfg/tarantool_silverbox_cfg.cfg index 4477cd880b9cf8aae197eb05cd76ad41ee4470dc..de8d1edb3e11d37d50ef0f03a2871435303cb78c 100644 --- a/cfg/tarantool_silverbox_cfg.cfg +++ b/cfg/tarantool_silverbox_cfg.cfg @@ -39,9 +39,6 @@ logger_nonblock = 1 # delay between loop iteraions io_collect_interval = 0 -# do not write snapshot faster then snap_io_rate_limit MBytes/sec -snap_io_rate_limit = 0 - # size of listen backlog backlog = 1024 @@ -79,6 +76,9 @@ memcached_expire_per_loop = 1024 # tarantool will try iterate all rows within this time memcached_expire_full_sweep = 3600 +# do not write snapshot faster then snap_io_rate_limit MBytes/sec +snap_io_rate_limit = 0 + # Write no more rows in WAL rows_per_wal = 500000 @@ -96,6 +96,11 @@ local_hot_standby = 0 # delay in fractional seconds between successive re-readings of wal_dir wal_dir_rescan_delay = 0.1 +# panic if where is error reading snap or wal +# be default panic any snapshot reading error and ignore errors then reading wals +panic_on_snap_error = 1 +panic_on_wal_error = 0 + # Remote hot standby (if enabled server will run in hot standby mode # continuously fetching WAL records from wal_feeder_ipaddr:wal_feeder_port remote_hot_standby = 0 @@ -108,8 +113,14 @@ namespace = [ estimated_rows = 0 index = [ { - type = "NUM" - key_position = -1 + type = "" + unique = -1 + key_field = [ + { + fieldno = -1 + type = "" + } + ] } ] } diff --git a/cfg/tarantool_silverbox_cfg.cfg_tmpl b/cfg/tarantool_silverbox_cfg.cfg_tmpl index 2de00d690252e5f7f3558a0cc06cc34c92cf3763..444d681863f65c09089cae2923c0d7424b930969 100644 --- a/cfg/tarantool_silverbox_cfg.cfg_tmpl +++ b/cfg/tarantool_silverbox_cfg.cfg_tmpl @@ -42,9 +42,6 @@ logger_nonblock=1 # delay between loop iteraions io_collect_interval=0.0 -# do not write snapshot faster then snap_io_rate_limit MBytes/sec -snap_io_rate_limit=0.0 - # size of listen backlog backlog=1024 @@ -79,6 +76,10 @@ memcached_expire_per_loop=1024 # tarantool will try iterate all rows within this time memcached_expire_full_sweep=3600 + +# do not write snapshot faster then snap_io_rate_limit MBytes/sec +snap_io_rate_limit=0.0 + # Write no more rows in WAL rows_per_wal=500000 @@ -95,6 +96,12 @@ local_hot_standby=0 # delay in fractional seconds between successive re-readings of wal_dir wal_dir_rescan_delay=0.1 + +# panic if where is error reading snap or wal +# be default panic any snapshot reading error and ignore errors then reading wals +panic_on_snap_error=1 +panic_on_wal_error=0 + # Remote hot standby (if enabled server will run in hot standby mode # continuously fetching WAL records from wal_feeder_ipaddr:wal_feeder_port remote_hot_standby=0 @@ -107,7 +114,11 @@ namespace = [ cardinality = -1 estimated_rows = 0 index = [ - type = "NUM" - key_position = -1 + type = "" + unique = -1 + key_field = [ + fieldno = -1 + type = "" + ] ] -] \ No newline at end of file +] diff --git a/cfg/tarantool_silverbox_cfg.h b/cfg/tarantool_silverbox_cfg.h index 98d51bf16e16dcca3942463eb5543a175c0f635c..ae2d7add9a067cd0d1df67099ba39db2b15d23fe 100644 --- a/cfg/tarantool_silverbox_cfg.h +++ b/cfg/tarantool_silverbox_cfg.h @@ -8,9 +8,15 @@ * Autogenerated file, do not edit it! */ +typedef struct tarantool_cfg_namespace_index_key_field { + int32_t fieldno; + char* type; +} tarantool_cfg_namespace_index_key_field; + typedef struct tarantool_cfg_namespace_index { char* type; - int32_t key_position; + int32_t unique; + tarantool_cfg_namespace_index_key_field** key_field; } tarantool_cfg_namespace_index; typedef struct tarantool_cfg_namespace { @@ -66,9 +72,6 @@ typedef struct tarantool_cfg { /* delay between loop iteraions */ double io_collect_interval; - /* do not write snapshot faster then snap_io_rate_limit MBytes/sec */ - double snap_io_rate_limit; - /* size of listen backlog */ int32_t backlog; @@ -108,6 +111,9 @@ typedef struct tarantool_cfg { /* tarantool will try iterate all rows within this time */ int32_t memcached_expire_full_sweep; + /* do not write snapshot faster then snap_io_rate_limit MBytes/sec */ + double snap_io_rate_limit; + /* Write no more rows in WAL */ int32_t rows_per_wal; @@ -129,6 +135,13 @@ typedef struct tarantool_cfg { /* delay in fractional seconds between successive re-readings of wal_dir */ double wal_dir_rescan_delay; + /* + * panic if where is error reading snap or wal + * be default panic any snapshot reading error and ignore errors then reading wals + */ + int32_t panic_on_snap_error; + int32_t panic_on_wal_error; + /* * Remote hot standby (if enabled server will run in hot standby mode * continuously fetching WAL records from wal_feeder_ipaddr:wal_feeder_port @@ -144,6 +157,8 @@ void parse_cfg_file_tarantool_cfg(tarantool_cfg *c, FILE *fh, int check_rdonly, void parse_cfg_buffer_tarantool_cfg(tarantool_cfg *c, char *buffer, int check_rdonly, int *n_accepted, int *n_skipped); +int check_cfg_tarantool_cfg(tarantool_cfg *c); + typedef struct tarantool_cfg_iterator_t tarantool_cfg_iterator_t; tarantool_cfg_iterator_t* tarantool_cfg_iterator_init(); char* tarantool_cfg_iterator_next(tarantool_cfg_iterator_t* i, tarantool_cfg *c, char **v); diff --git a/cfg/warning.c b/cfg/warning.c index 1318f951b58864310d97227b58bdb9ca7547c57d..fbfbf0fc8a929234416c7a84c1d498214651bab5 100644 --- a/cfg/warning.c +++ b/cfg/warning.c @@ -10,5 +10,14 @@ out_warning(ConfettyError v, char *format, ...) (void)v; /* make gcc happy */ va_list ap; va_start(ap, format); - vsay(S_WARN, NULL, format, ap); + switch (v) { + case CNF_NOTSET: + vsay(S_FATAL, NULL, format, ap); + panic("can't read config"); + + break; + + default: + vsay(S_WARN, NULL, format, ap); + } } diff --git a/core/admin.c b/core/admin.c index 3081f6122cf43d0b7d50e5f50e84c58bd991de52..d4923b101af8913acf6f2eb13c3f5188ee9c8c44 100644 --- a/core/admin.c +++ b/core/admin.c @@ -38,12 +38,13 @@ #include <say.h> #include <stat.h> #include <tarantool.h> +#include <tbuf.h> #include <util.h> static const char help[] = "available commands:\r\n" "help\r\n" - "quit\r\n" + "exit\r\n" "show info\r\n" "show fiber\r\n" "show configuration\r\n" @@ -51,21 +52,23 @@ static const char help[] = "show palloc\r\n" "show stat\r\n" "save coredump\r\n" - "save snapshot\r\n"; + "save snapshot\r\n" + "exec module command\r\n" + ; static const char unknown_command[] = "unknown command. try typing help.\r\n"; -#line 61 "core/admin.c" +#line 64 "core/admin.c" static const int admin_start = 1; -static const int admin_first_final = 81; +static const int admin_first_final = 88; static const int admin_error = 0; static const int admin_en_main = 1; -#line 60 "core/admin.rl" +#line 63 "core/admin.rl" static void @@ -86,6 +89,7 @@ admin_dispatch(void) struct tbuf *out = tbuf_alloc(fiber->pool); int cs; char *p, *pe; + char *strstart, *strend; while ((pe = memchr(fiber->rbuf->data, '\n', fiber->rbuf->len)) == NULL) { if (fiber_bread(fiber->rbuf, 1) <= 0) @@ -96,12 +100,12 @@ admin_dispatch(void) p = fiber->rbuf->data; -#line 100 "core/admin.c" +#line 104 "core/admin.c" { cs = admin_start; } -#line 105 "core/admin.c" +#line 109 "core/admin.c" { if ( p == pe ) goto _test_eof; @@ -110,9 +114,10 @@ admin_dispatch(void) case 1: switch( (*p) ) { case 99: goto st2; - case 104: goto st13; - case 113: goto st17; - case 115: goto st21; + case 101: goto st13; + case 104: goto st22; + case 113: goto st26; + case 115: goto st28; } goto st0; st0: @@ -138,8 +143,10 @@ case 3: if ( ++p == pe ) goto _test_eof4; case 4: - if ( (*p) == 115 ) - goto st5; + switch( (*p) ) { + case 32: goto st4; + case 115: goto st5; + } goto st0; st5: if ( ++p == pe ) @@ -153,33 +160,39 @@ case 5: goto _test_eof6; case 6: switch( (*p) ) { - case 10: goto tr10; - case 13: goto tr11; + case 10: goto tr11; + case 13: goto tr12; case 97: goto st8; } goto st0; -tr10: -#line 133 "core/admin.rl" +tr11: +#line 140 "core/admin.rl" {slab_validate(); ok(out);} - goto st81; -tr17: -#line 123 "core/admin.rl" - {tbuf_append(out, help, sizeof(help));} - goto st81; -tr22: -#line 124 "core/admin.rl" + goto st88; +tr18: +#line 130 "core/admin.rl" {return 0;} - goto st81; -tr34: -#line 131 "core/admin.rl" + goto st88; +tr27: +#line 127 "core/admin.rl" + {strend = p;} +#line 139 "core/admin.rl" + {mod_exec(strstart, strend - strstart, out); end(out);} + goto st88; +tr31: +#line 129 "core/admin.rl" + {tbuf_append(out, help, sizeof(help));} + goto st88; +tr44: +#line 137 "core/admin.rl" {coredump(60); ok(out);} - goto st81; -tr43: -#line 132 "core/admin.rl" + goto st88; +tr53: +#line 138 "core/admin.rl" {snapshot(NULL, 0); ok(out);} - goto st81; -tr60: -#line 90 "core/admin.rl" + goto st88; +tr70: +#line 94 "core/admin.rl" { tarantool_cfg_iterator_t *i; char *key, *value; @@ -196,55 +209,61 @@ case 6: } end(out); } - goto st81; -tr74: -#line 126 "core/admin.rl" + goto st88; +tr84: +#line 132 "core/admin.rl" {fiber_info(out);end(out);} - goto st81; -tr80: -#line 125 "core/admin.rl" + goto st88; +tr90: +#line 131 "core/admin.rl" {mod_info(out); end(out);} - goto st81; -tr85: -#line 129 "core/admin.rl" + goto st88; +tr95: +#line 135 "core/admin.rl" {palloc_stat(out);end(out);} - goto st81; -tr93: -#line 128 "core/admin.rl" + goto st88; +tr103: +#line 134 "core/admin.rl" {slab_stat(out);end(out);} - goto st81; -tr97: -#line 130 "core/admin.rl" + goto st88; +tr107: +#line 136 "core/admin.rl" {stat_print(out);end(out);} - goto st81; -st81: + goto st88; +st88: if ( ++p == pe ) - goto _test_eof81; -case 81: -#line 225 "core/admin.c" + goto _test_eof88; +case 88: +#line 238 "core/admin.c" goto st0; -tr11: -#line 133 "core/admin.rl" +tr12: +#line 140 "core/admin.rl" {slab_validate(); ok(out);} goto st7; -tr18: -#line 123 "core/admin.rl" - {tbuf_append(out, help, sizeof(help));} - goto st7; -tr23: -#line 124 "core/admin.rl" +tr19: +#line 130 "core/admin.rl" {return 0;} goto st7; -tr35: -#line 131 "core/admin.rl" +tr28: +#line 127 "core/admin.rl" + {strend = p;} +#line 139 "core/admin.rl" + {mod_exec(strstart, strend - strstart, out); end(out);} + goto st7; +tr32: +#line 129 "core/admin.rl" + {tbuf_append(out, help, sizeof(help));} + goto st7; +tr45: +#line 137 "core/admin.rl" {coredump(60); ok(out);} goto st7; -tr44: -#line 132 "core/admin.rl" +tr54: +#line 138 "core/admin.rl" {snapshot(NULL, 0); ok(out);} goto st7; -tr61: -#line 90 "core/admin.rl" +tr71: +#line 94 "core/admin.rl" { tarantool_cfg_iterator_t *i; char *key, *value; @@ -262,41 +281,41 @@ case 81: end(out); } goto st7; -tr75: -#line 126 "core/admin.rl" +tr85: +#line 132 "core/admin.rl" {fiber_info(out);end(out);} goto st7; -tr81: -#line 125 "core/admin.rl" +tr91: +#line 131 "core/admin.rl" {mod_info(out); end(out);} goto st7; -tr86: -#line 129 "core/admin.rl" +tr96: +#line 135 "core/admin.rl" {palloc_stat(out);end(out);} goto st7; -tr94: -#line 128 "core/admin.rl" +tr104: +#line 134 "core/admin.rl" {slab_stat(out);end(out);} goto st7; -tr98: -#line 130 "core/admin.rl" +tr108: +#line 136 "core/admin.rl" {stat_print(out);end(out);} goto st7; st7: if ( ++p == pe ) goto _test_eof7; case 7: -#line 290 "core/admin.c" +#line 309 "core/admin.c" if ( (*p) == 10 ) - goto st81; + goto st88; goto st0; st8: if ( ++p == pe ) goto _test_eof8; case 8: switch( (*p) ) { - case 10: goto tr10; - case 13: goto tr11; + case 10: goto tr11; + case 13: goto tr12; case 98: goto st9; } goto st0; @@ -305,8 +324,8 @@ case 8: goto _test_eof9; case 9: switch( (*p) ) { - case 10: goto tr10; - case 13: goto tr11; + case 10: goto tr11; + case 13: goto tr12; } goto st0; st10: @@ -339,9 +358,9 @@ case 12: goto _test_eof13; case 13: switch( (*p) ) { - case 10: goto tr17; - case 13: goto tr18; - case 101: goto st14; + case 10: goto tr18; + case 13: goto tr19; + case 120: goto st14; } goto st0; st14: @@ -349,9 +368,11 @@ case 13: goto _test_eof14; case 14: switch( (*p) ) { - case 10: goto tr17; - case 13: goto tr18; - case 108: goto st15; + case 10: goto tr18; + case 13: goto tr19; + case 32: goto st15; + case 101: goto st18; + case 105: goto st20; } goto st0; st15: @@ -359,57 +380,64 @@ case 14: goto _test_eof15; case 15: switch( (*p) ) { - case 10: goto tr17; - case 13: goto tr18; - case 112: goto st16; + case 10: goto st0; + case 13: goto st0; + case 32: goto tr25; } - goto st0; + goto tr24; +tr24: +#line 127 "core/admin.rl" + {strstart = p;} + goto st16; st16: if ( ++p == pe ) goto _test_eof16; case 16: +#line 397 "core/admin.c" switch( (*p) ) { - case 10: goto tr17; - case 13: goto tr18; + case 10: goto tr27; + case 13: goto tr28; } - goto st0; + goto st16; +tr25: +#line 127 "core/admin.rl" + {strstart = p;} + goto st17; st17: if ( ++p == pe ) goto _test_eof17; case 17: +#line 411 "core/admin.c" switch( (*p) ) { - case 10: goto tr22; - case 13: goto tr23; - case 117: goto st18; + case 10: goto tr27; + case 13: goto tr28; + case 32: goto tr25; } - goto st0; + goto tr24; st18: if ( ++p == pe ) goto _test_eof18; case 18: switch( (*p) ) { - case 10: goto tr22; - case 13: goto tr23; - case 105: goto st19; + case 32: goto st15; + case 99: goto st19; } goto st0; st19: if ( ++p == pe ) goto _test_eof19; case 19: - switch( (*p) ) { - case 10: goto tr22; - case 13: goto tr23; - case 116: goto st20; - } + if ( (*p) == 32 ) + goto st15; goto st0; st20: if ( ++p == pe ) goto _test_eof20; case 20: switch( (*p) ) { - case 10: goto tr22; - case 13: goto tr23; + case 10: goto tr18; + case 13: goto tr19; + case 116: goto st21; } goto st0; st21: @@ -417,8 +445,8 @@ case 20: goto _test_eof21; case 21: switch( (*p) ) { - case 97: goto st22; - case 104: goto st42; + case 10: goto tr18; + case 13: goto tr19; } goto st0; st22: @@ -426,8 +454,9 @@ case 21: goto _test_eof22; case 22: switch( (*p) ) { - case 32: goto st23; - case 118: goto st40; + case 10: goto tr31; + case 13: goto tr32; + case 101: goto st23; } goto st0; st23: @@ -435,25 +464,28 @@ case 22: goto _test_eof23; case 23: switch( (*p) ) { - case 99: goto st24; - case 115: goto st32; + case 10: goto tr31; + case 13: goto tr32; + case 108: goto st24; } goto st0; st24: if ( ++p == pe ) goto _test_eof24; case 24: - if ( (*p) == 111 ) - goto st25; + switch( (*p) ) { + case 10: goto tr31; + case 13: goto tr32; + case 112: goto st25; + } goto st0; st25: if ( ++p == pe ) goto _test_eof25; case 25: switch( (*p) ) { - case 10: goto tr34; - case 13: goto tr35; - case 114: goto st26; + case 10: goto tr31; + case 13: goto tr32; } goto st0; st26: @@ -461,9 +493,9 @@ case 25: goto _test_eof26; case 26: switch( (*p) ) { - case 10: goto tr34; - case 13: goto tr35; - case 101: goto st27; + case 10: goto tr18; + case 13: goto tr19; + case 117: goto st27; } goto st0; st27: @@ -471,9 +503,9 @@ case 26: goto _test_eof27; case 27: switch( (*p) ) { - case 10: goto tr34; - case 13: goto tr35; - case 100: goto st28; + case 10: goto tr18; + case 13: goto tr19; + case 105: goto st20; } goto st0; st28: @@ -481,9 +513,8 @@ case 27: goto _test_eof28; case 28: switch( (*p) ) { - case 10: goto tr34; - case 13: goto tr35; - case 117: goto st29; + case 97: goto st29; + case 104: goto st49; } goto st0; st29: @@ -491,9 +522,8 @@ case 28: goto _test_eof29; case 29: switch( (*p) ) { - case 10: goto tr34; - case 13: goto tr35; - case 109: goto st30; + case 32: goto st30; + case 118: goto st47; } goto st0; st30: @@ -501,35 +531,36 @@ case 29: goto _test_eof30; case 30: switch( (*p) ) { - case 10: goto tr34; - case 13: goto tr35; - case 112: goto st31; + case 32: goto st30; + case 99: goto st31; + case 115: goto st39; } goto st0; st31: if ( ++p == pe ) goto _test_eof31; case 31: - switch( (*p) ) { - case 10: goto tr34; - case 13: goto tr35; - } + if ( (*p) == 111 ) + goto st32; goto st0; st32: if ( ++p == pe ) goto _test_eof32; case 32: - if ( (*p) == 110 ) - goto st33; + switch( (*p) ) { + case 10: goto tr44; + case 13: goto tr45; + case 114: goto st33; + } goto st0; st33: if ( ++p == pe ) goto _test_eof33; case 33: switch( (*p) ) { - case 10: goto tr43; - case 13: goto tr44; - case 97: goto st34; + case 10: goto tr44; + case 13: goto tr45; + case 101: goto st34; } goto st0; st34: @@ -537,9 +568,9 @@ case 33: goto _test_eof34; case 34: switch( (*p) ) { - case 10: goto tr43; - case 13: goto tr44; - case 112: goto st35; + case 10: goto tr44; + case 13: goto tr45; + case 100: goto st35; } goto st0; st35: @@ -547,9 +578,9 @@ case 34: goto _test_eof35; case 35: switch( (*p) ) { - case 10: goto tr43; - case 13: goto tr44; - case 115: goto st36; + case 10: goto tr44; + case 13: goto tr45; + case 117: goto st36; } goto st0; st36: @@ -557,9 +588,9 @@ case 35: goto _test_eof36; case 36: switch( (*p) ) { - case 10: goto tr43; - case 13: goto tr44; - case 104: goto st37; + case 10: goto tr44; + case 13: goto tr45; + case 109: goto st37; } goto st0; st37: @@ -567,9 +598,9 @@ case 36: goto _test_eof37; case 37: switch( (*p) ) { - case 10: goto tr43; - case 13: goto tr44; - case 111: goto st38; + case 10: goto tr44; + case 13: goto tr45; + case 112: goto st38; } goto st0; st38: @@ -577,43 +608,45 @@ case 37: goto _test_eof38; case 38: switch( (*p) ) { - case 10: goto tr43; - case 13: goto tr44; - case 116: goto st39; + case 10: goto tr44; + case 13: goto tr45; } goto st0; st39: if ( ++p == pe ) goto _test_eof39; case 39: - switch( (*p) ) { - case 10: goto tr43; - case 13: goto tr44; - } + if ( (*p) == 110 ) + goto st40; goto st0; st40: if ( ++p == pe ) goto _test_eof40; case 40: switch( (*p) ) { - case 32: goto st23; - case 101: goto st41; + case 10: goto tr53; + case 13: goto tr54; + case 97: goto st41; } goto st0; st41: if ( ++p == pe ) goto _test_eof41; case 41: - if ( (*p) == 32 ) - goto st23; + switch( (*p) ) { + case 10: goto tr53; + case 13: goto tr54; + case 112: goto st42; + } goto st0; st42: if ( ++p == pe ) goto _test_eof42; case 42: switch( (*p) ) { - case 32: goto st43; - case 111: goto st79; + case 10: goto tr53; + case 13: goto tr54; + case 115: goto st43; } goto st0; st43: @@ -621,28 +654,29 @@ case 42: goto _test_eof43; case 43: switch( (*p) ) { - case 99: goto st44; - case 102: goto st57; - case 105: goto st62; - case 112: goto st66; - case 115: goto st72; + case 10: goto tr53; + case 13: goto tr54; + case 104: goto st44; } goto st0; st44: if ( ++p == pe ) goto _test_eof44; case 44: - if ( (*p) == 111 ) - goto st45; + switch( (*p) ) { + case 10: goto tr53; + case 13: goto tr54; + case 111: goto st45; + } goto st0; st45: if ( ++p == pe ) goto _test_eof45; case 45: switch( (*p) ) { - case 10: goto tr60; - case 13: goto tr61; - case 110: goto st46; + case 10: goto tr53; + case 13: goto tr54; + case 116: goto st46; } goto st0; st46: @@ -650,9 +684,8 @@ case 45: goto _test_eof46; case 46: switch( (*p) ) { - case 10: goto tr60; - case 13: goto tr61; - case 102: goto st47; + case 10: goto tr53; + case 13: goto tr54; } goto st0; st47: @@ -660,29 +693,24 @@ case 46: goto _test_eof47; case 47: switch( (*p) ) { - case 10: goto tr60; - case 13: goto tr61; - case 105: goto st48; + case 32: goto st30; + case 101: goto st48; } goto st0; st48: if ( ++p == pe ) goto _test_eof48; case 48: - switch( (*p) ) { - case 10: goto tr60; - case 13: goto tr61; - case 103: goto st49; - } + if ( (*p) == 32 ) + goto st30; goto st0; st49: if ( ++p == pe ) goto _test_eof49; case 49: switch( (*p) ) { - case 10: goto tr60; - case 13: goto tr61; - case 117: goto st50; + case 32: goto st50; + case 111: goto st86; } goto st0; st50: @@ -690,29 +718,29 @@ case 49: goto _test_eof50; case 50: switch( (*p) ) { - case 10: goto tr60; - case 13: goto tr61; - case 114: goto st51; + case 32: goto st50; + case 99: goto st51; + case 102: goto st64; + case 105: goto st69; + case 112: goto st73; + case 115: goto st79; } goto st0; st51: if ( ++p == pe ) goto _test_eof51; case 51: - switch( (*p) ) { - case 10: goto tr60; - case 13: goto tr61; - case 97: goto st52; - } + if ( (*p) == 111 ) + goto st52; goto st0; st52: if ( ++p == pe ) goto _test_eof52; case 52: switch( (*p) ) { - case 10: goto tr60; - case 13: goto tr61; - case 116: goto st53; + case 10: goto tr70; + case 13: goto tr71; + case 110: goto st53; } goto st0; st53: @@ -720,9 +748,9 @@ case 52: goto _test_eof53; case 53: switch( (*p) ) { - case 10: goto tr60; - case 13: goto tr61; - case 105: goto st54; + case 10: goto tr70; + case 13: goto tr71; + case 102: goto st54; } goto st0; st54: @@ -730,9 +758,9 @@ case 53: goto _test_eof54; case 54: switch( (*p) ) { - case 10: goto tr60; - case 13: goto tr61; - case 111: goto st55; + case 10: goto tr70; + case 13: goto tr71; + case 105: goto st55; } goto st0; st55: @@ -740,9 +768,9 @@ case 54: goto _test_eof55; case 55: switch( (*p) ) { - case 10: goto tr60; - case 13: goto tr61; - case 110: goto st56; + case 10: goto tr70; + case 13: goto tr71; + case 103: goto st56; } goto st0; st56: @@ -750,25 +778,29 @@ case 55: goto _test_eof56; case 56: switch( (*p) ) { - case 10: goto tr60; - case 13: goto tr61; + case 10: goto tr70; + case 13: goto tr71; + case 117: goto st57; } goto st0; st57: if ( ++p == pe ) goto _test_eof57; case 57: - if ( (*p) == 105 ) - goto st58; + switch( (*p) ) { + case 10: goto tr70; + case 13: goto tr71; + case 114: goto st58; + } goto st0; st58: if ( ++p == pe ) goto _test_eof58; case 58: switch( (*p) ) { - case 10: goto tr74; - case 13: goto tr75; - case 98: goto st59; + case 10: goto tr70; + case 13: goto tr71; + case 97: goto st59; } goto st0; st59: @@ -776,9 +808,9 @@ case 58: goto _test_eof59; case 59: switch( (*p) ) { - case 10: goto tr74; - case 13: goto tr75; - case 101: goto st60; + case 10: goto tr70; + case 13: goto tr71; + case 116: goto st60; } goto st0; st60: @@ -786,9 +818,9 @@ case 59: goto _test_eof60; case 60: switch( (*p) ) { - case 10: goto tr74; - case 13: goto tr75; - case 114: goto st61; + case 10: goto tr70; + case 13: goto tr71; + case 105: goto st61; } goto st0; st61: @@ -796,61 +828,65 @@ case 60: goto _test_eof61; case 61: switch( (*p) ) { - case 10: goto tr74; - case 13: goto tr75; + case 10: goto tr70; + case 13: goto tr71; + case 111: goto st62; } goto st0; st62: if ( ++p == pe ) goto _test_eof62; case 62: - if ( (*p) == 110 ) - goto st63; + switch( (*p) ) { + case 10: goto tr70; + case 13: goto tr71; + case 110: goto st63; + } goto st0; st63: if ( ++p == pe ) goto _test_eof63; case 63: switch( (*p) ) { - case 10: goto tr80; - case 13: goto tr81; - case 102: goto st64; + case 10: goto tr70; + case 13: goto tr71; } goto st0; st64: if ( ++p == pe ) goto _test_eof64; case 64: - switch( (*p) ) { - case 10: goto tr80; - case 13: goto tr81; - case 111: goto st65; - } + if ( (*p) == 105 ) + goto st65; goto st0; st65: if ( ++p == pe ) goto _test_eof65; case 65: switch( (*p) ) { - case 10: goto tr80; - case 13: goto tr81; + case 10: goto tr84; + case 13: goto tr85; + case 98: goto st66; } goto st0; st66: if ( ++p == pe ) goto _test_eof66; case 66: - if ( (*p) == 97 ) - goto st67; + switch( (*p) ) { + case 10: goto tr84; + case 13: goto tr85; + case 101: goto st67; + } goto st0; st67: if ( ++p == pe ) goto _test_eof67; case 67: switch( (*p) ) { - case 10: goto tr85; - case 13: goto tr86; - case 108: goto st68; + case 10: goto tr84; + case 13: goto tr85; + case 114: goto st68; } goto st0; st68: @@ -858,29 +894,25 @@ case 67: goto _test_eof68; case 68: switch( (*p) ) { - case 10: goto tr85; - case 13: goto tr86; - case 108: goto st69; + case 10: goto tr84; + case 13: goto tr85; } goto st0; st69: if ( ++p == pe ) goto _test_eof69; case 69: - switch( (*p) ) { - case 10: goto tr85; - case 13: goto tr86; - case 111: goto st70; - } + if ( (*p) == 110 ) + goto st70; goto st0; st70: if ( ++p == pe ) goto _test_eof70; case 70: switch( (*p) ) { - case 10: goto tr85; - case 13: goto tr86; - case 99: goto st71; + case 10: goto tr90; + case 13: goto tr91; + case 102: goto st71; } goto st0; st71: @@ -888,8 +920,9 @@ case 70: goto _test_eof71; case 71: switch( (*p) ) { - case 10: goto tr85; - case 13: goto tr86; + case 10: goto tr90; + case 13: goto tr91; + case 111: goto st72; } goto st0; st72: @@ -897,28 +930,25 @@ case 71: goto _test_eof72; case 72: switch( (*p) ) { - case 108: goto st73; - case 116: goto st76; + case 10: goto tr90; + case 13: goto tr91; } goto st0; st73: if ( ++p == pe ) goto _test_eof73; case 73: - switch( (*p) ) { - case 10: goto tr93; - case 13: goto tr94; - case 97: goto st74; - } + if ( (*p) == 97 ) + goto st74; goto st0; st74: if ( ++p == pe ) goto _test_eof74; case 74: switch( (*p) ) { - case 10: goto tr93; - case 13: goto tr94; - case 98: goto st75; + case 10: goto tr95; + case 13: goto tr96; + case 108: goto st75; } goto st0; st75: @@ -926,8 +956,9 @@ case 74: goto _test_eof75; case 75: switch( (*p) ) { - case 10: goto tr93; - case 13: goto tr94; + case 10: goto tr95; + case 13: goto tr96; + case 108: goto st76; } goto st0; st76: @@ -935,9 +966,9 @@ case 75: goto _test_eof76; case 76: switch( (*p) ) { - case 10: goto tr97; - case 13: goto tr98; - case 97: goto st77; + case 10: goto tr95; + case 13: goto tr96; + case 111: goto st77; } goto st0; st77: @@ -945,9 +976,9 @@ case 76: goto _test_eof77; case 77: switch( (*p) ) { - case 10: goto tr97; - case 13: goto tr98; - case 116: goto st78; + case 10: goto tr95; + case 13: goto tr96; + case 99: goto st78; } goto st0; st78: @@ -955,8 +986,8 @@ case 77: goto _test_eof78; case 78: switch( (*p) ) { - case 10: goto tr97; - case 13: goto tr98; + case 10: goto tr95; + case 13: goto tr96; } goto st0; st79: @@ -964,16 +995,83 @@ case 78: goto _test_eof79; case 79: switch( (*p) ) { - case 32: goto st43; - case 119: goto st80; + case 108: goto st80; + case 116: goto st83; } goto st0; st80: if ( ++p == pe ) goto _test_eof80; case 80: + switch( (*p) ) { + case 10: goto tr103; + case 13: goto tr104; + case 97: goto st81; + } + goto st0; +st81: + if ( ++p == pe ) + goto _test_eof81; +case 81: + switch( (*p) ) { + case 10: goto tr103; + case 13: goto tr104; + case 98: goto st82; + } + goto st0; +st82: + if ( ++p == pe ) + goto _test_eof82; +case 82: + switch( (*p) ) { + case 10: goto tr103; + case 13: goto tr104; + } + goto st0; +st83: + if ( ++p == pe ) + goto _test_eof83; +case 83: + switch( (*p) ) { + case 10: goto tr107; + case 13: goto tr108; + case 97: goto st84; + } + goto st0; +st84: + if ( ++p == pe ) + goto _test_eof84; +case 84: + switch( (*p) ) { + case 10: goto tr107; + case 13: goto tr108; + case 116: goto st85; + } + goto st0; +st85: + if ( ++p == pe ) + goto _test_eof85; +case 85: + switch( (*p) ) { + case 10: goto tr107; + case 13: goto tr108; + } + goto st0; +st86: + if ( ++p == pe ) + goto _test_eof86; +case 86: + switch( (*p) ) { + case 32: goto st50; + case 119: goto st87; + } + goto st0; +st87: + if ( ++p == pe ) + goto _test_eof87; +case 87: if ( (*p) == 32 ) - goto st43; + goto st50; goto st0; } _test_eof2: cs = 2; goto _test_eof; @@ -981,7 +1079,7 @@ case 80: _test_eof4: cs = 4; goto _test_eof; _test_eof5: cs = 5; goto _test_eof; _test_eof6: cs = 6; goto _test_eof; - _test_eof81: cs = 81; goto _test_eof; + _test_eof88: cs = 88; goto _test_eof; _test_eof7: cs = 7; goto _test_eof; _test_eof8: cs = 8; goto _test_eof; _test_eof9: cs = 9; goto _test_eof; @@ -1056,12 +1154,19 @@ case 80: _test_eof78: cs = 78; goto _test_eof; _test_eof79: cs = 79; goto _test_eof; _test_eof80: cs = 80; goto _test_eof; + _test_eof81: cs = 81; goto _test_eof; + _test_eof82: cs = 82; goto _test_eof; + _test_eof83: cs = 83; goto _test_eof; + _test_eof84: cs = 84; goto _test_eof; + _test_eof85: cs = 85; goto _test_eof; + _test_eof86: cs = 86; goto _test_eof; + _test_eof87: cs = 87; goto _test_eof; _test_eof: {} _out: {} } -#line 138 "core/admin.rl" +#line 145 "core/admin.rl" fiber->rbuf->len -= (void *)pe - (void *)fiber->rbuf->data; diff --git a/core/admin.rl b/core/admin.rl index ca4c6d05d00b55348fec134be95b3ac39102ecc4..a73061c705f18e6f42652bc3689a6bd95280101f 100644 --- a/core/admin.rl +++ b/core/admin.rl @@ -36,12 +36,13 @@ #include <say.h> #include <stat.h> #include <tarantool.h> +#include <tbuf.h> #include <util.h> static const char help[] = "available commands:\r\n" "help\r\n" - "quit\r\n" + "exit\r\n" "show info\r\n" "show fiber\r\n" "show configuration\r\n" @@ -49,7 +50,9 @@ static const char help[] = "show palloc\r\n" "show stat\r\n" "save coredump\r\n" - "save snapshot\r\n"; + "save snapshot\r\n" + "exec module command\r\n" + ; static const char unknown_command[] = "unknown command. try typing help.\r\n"; @@ -77,6 +80,7 @@ admin_dispatch(void) struct tbuf *out = tbuf_alloc(fiber->pool); int cs; char *p, *pe; + char *strstart, *strend; while ((pe = memchr(fiber->rbuf->data, '\n', fiber->rbuf->len)) == NULL) { if (fiber_bread(fiber->rbuf, 1) <= 0) @@ -115,22 +119,25 @@ admin_dispatch(void) palloc = "pa"("l"("l"("o"("c")?)?)?)?; stat = "st"("a"("t")?)?; help = "h"("e"("l"("p")?)?)?; - quit = "q"("u"("i"("t")?)?)?; + exit = "e"("x"("i"("t")?)?)? | "q"("u"("i"("t")?)?)?; save = "sa"("v"("e")?)?; coredump = "co"("r"("e"("d"("u"("m"("p")?)?)?)?)?)?; snapshot = "sn"("a"("p"("s"("h"("o"("t")?)?)?)?)?)?; - - commands = (help %{tbuf_append(out, help, sizeof(help));}| - quit %{return 0;} | - show " " info %{mod_info(out); end(out);} | - show " " fiber %{fiber_info(out);end(out);} | - show " " configuration %show_configuration | - show " " slab %{slab_stat(out);end(out);} | - show " " palloc %{palloc_stat(out);end(out);} | - show " " stat %{stat_print(out);end(out);} | - save " " coredump %{coredump(60); ok(out);} | - save " " snapshot %{snapshot(NULL, 0); ok(out);} | - check " " slab %{slab_validate(); ok(out);}); + exec = "ex"("e"("c")?)?; + string = [^\r\n]+ >{strstart = p;} %{strend = p;}; + + commands = (help %{tbuf_append(out, help, sizeof(help));} | + exit %{return 0;} | + show " "+ info %{mod_info(out); end(out);} | + show " "+ fiber %{fiber_info(out);end(out);} | + show " "+ configuration %show_configuration | + show " "+ slab %{slab_stat(out);end(out);} | + show " "+ palloc %{palloc_stat(out);end(out);} | + show " "+ stat %{stat_print(out);end(out);} | + save " "+ coredump %{coredump(60); ok(out);} | + save " "+ snapshot %{snapshot(NULL, 0); ok(out);} | + exec " "+ string %{mod_exec(strstart, strend - strstart, out); end(out);}| + check " "+ slab %{slab_validate(); ok(out);}); main := commands eol; write init; diff --git a/core/coro.c b/core/coro.c index 0d840cb82633aed445d1e3f09ea135d975706ed3..2d35cbe9b2d7f01afe292b2142aa8dc50cebe605 100644 --- a/core/coro.c +++ b/core/coro.c @@ -41,7 +41,7 @@ extern void *main_stack_frame; struct tarantool_coro * -tarantool_coro_create(struct tarantool_coro *coro, void (*f)(void *), void *data) +tarantool_coro_create(struct tarantool_coro *coro, void (*f) (void *), void *data) { const int page = sysconf(_SC_PAGESIZE); @@ -56,7 +56,7 @@ tarantool_coro_create(struct tarantool_coro *coro, void (*f)(void *), void *data /* TODO: guard pages */ coro->stack_size = page * 16; coro->stack = mmap(0, coro->stack_size, PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (coro->stack == MAP_FAILED) return NULL; diff --git a/core/fiber.c b/core/fiber.c index 72e6f0a07a8c5f2fb9e5c100414d461a01754de3..1e9b74136eb0da590d350a7f3b0446bb20bb1618 100644 --- a/core/fiber.c +++ b/core/fiber.c @@ -43,7 +43,6 @@ #include <third_party/khash.h> #include <debug.h> -#include <bert.h> #include <fiber.h> #include <palloc.h> #include <salloc.h> @@ -92,7 +91,6 @@ static khash_t(fid2fiber) * fibers_registry; - void fiber_call(struct fiber *callee) { @@ -172,7 +170,6 @@ ev_schedule(ev_watcher *watcher, int event __unused__) fiber_call(watcher->data); } - static struct fiber * fid2fiber(int fid) { @@ -251,6 +248,9 @@ fiber_gc(void) fiber_cleanup(); + if (palloc_allocated(fiber->pool) < 128 * 1024) + return; + tmp = fiber->pool; fiber->pool = ex_pool; ex_pool = tmp; @@ -295,7 +295,6 @@ fiber_zombificate(struct fiber *f) SLIST_INSERT_HEAD(&zombie_fibers, f, zombie_link); } - static void fiber_loop(void *data __unused__) { @@ -362,7 +361,6 @@ fiber_create(const char *restrict name, int fd, int inbox_size, void (*f) (void return fiber; } - char * fiber_peer_name(struct fiber *fiber) { @@ -386,6 +384,8 @@ fiber_peer_name(struct fiber *fiber) snprintf(fiber->peer_name, sizeof(fiber->peer_name), "%s:%d", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port)); + fiber->cookie = 0; + memcpy(&fiber->cookie, &peer, MIN(sizeof(peer), sizeof(fiber->cookie))); return fiber->peer_name; } @@ -412,13 +412,13 @@ ring_size(struct ring *inbox) } int -inbox_size(struct fiber * recipient) +inbox_size(struct fiber *recipient) { - return ring_size(recipient->inbox); + return ring_size(recipient->inbox); } void -wait_inbox(struct fiber * recipient) +wait_inbox(struct fiber *recipient) { while (ring_size(recipient->inbox) == 0) { recipient->reading_inbox = true; @@ -428,7 +428,7 @@ wait_inbox(struct fiber * recipient) } bool -write_inbox(struct fiber * recipient, struct tbuf * msg) +write_inbox(struct fiber *recipient, struct tbuf *msg) { struct ring *inbox = recipient->inbox; if (ring_size(inbox) == inbox->size - 1) @@ -485,8 +485,6 @@ fiber_bread(struct tbuf *buf, size_t at_least) return r; } - - void add_iov_dup(void *buf, size_t len) { @@ -531,7 +529,7 @@ fiber_flush_output(void) for (int i = 0; i < iov_cnt; i++) rem += iov[i].iov_len; - say_syserror("client unexpectedly gone, %"PRI_SZ" bytes unwritten", rem); + say_syserror("client unexpectedly gone, %" PRI_SZ " bytes unwritten", rem); result = r; } else result = bytes; @@ -602,7 +600,7 @@ fiber_connect(struct sockaddr_in *addr) if (set_nonblock(fiber->fd) < 0) goto error; - if (connect(fiber->fd, (struct sockaddr*)addr, sizeof(*addr)) < 0) { + if (connect(fiber->fd, (struct sockaddr *)addr, sizeof(*addr)) < 0) { if (errno != EINPROGRESS) goto error; } @@ -620,7 +618,7 @@ fiber_connect(struct sockaddr_in *addr) unwait(EV_WRITE); return fiber->fd; -error: + error: unwait(EV_WRITE); fiber_close(); return fiber->fd; @@ -687,7 +685,6 @@ blocking_loop(int fd, struct tbuf *(*handler) (void *state, struct tbuf *), void break; } *request_size = ntohl(*request_size); - assert(*request_size < 4 * 1024 * 1024); if (read_atleast(fd, request, *request_size) < 0) { result = EXIT_SUCCESS; @@ -792,7 +789,8 @@ sock2inbox(void *_data __unused__) } struct child * -spawn_child(const char *name, int inbox_size, struct tbuf *(*handler) (void *, struct tbuf *), void *state) +spawn_child(const char *name, int inbox_size, struct tbuf *(*handler) (void *, struct tbuf *), + void *state) { char *proxy_name, *child_name; int socks[2]; @@ -887,7 +885,7 @@ tcp_server_handler(void *data) say_info("bound to TCP port %i", server->port); break; - sleep_and_retry: + sleep_and_retry: if (!warning_said) { say_warn("port %i is already in use, " "will retry binding after 0.1 seconds.", server->port); @@ -959,7 +957,7 @@ udp_server_handler(void *data) say_info("bound to UDP port %i", server->port); break; - sleep_and_retry: + sleep_and_retry: if (!warning_said) { say_warn("port %i is already in use, " "will retry binding after 0.1 seconds.", server->port); @@ -973,16 +971,17 @@ udp_server_handler(void *data) while (1) { #define MAXUDPPACKETLEN 128 - char buf[MAXUDPPACKETLEN]; - struct sockaddr_in addr; - socklen_t addrlen; - ssize_t sz; + char buf[MAXUDPPACKETLEN]; + struct sockaddr_in addr; + socklen_t addrlen; + ssize_t sz; wait_for(EV_READ); - for(;;) { + for (;;) { addrlen = sizeof(addr); - sz = recvfrom(fiber->fd, buf, MAXUDPPACKETLEN, MSG_DONTWAIT, (struct sockaddr *)&addr, &addrlen); + sz = recvfrom(fiber->fd, buf, MAXUDPPACKETLEN, MSG_DONTWAIT, + (struct sockaddr *)&addr, &addrlen); if (sz <= 0) { if (!(errno == EAGAIN || errno == EWOULDBLOCK)) @@ -992,7 +991,7 @@ udp_server_handler(void *data) if (server->handler) { server->handler(data); } else { - void (*f)(char *, int) = data; + void (*f) (char *, int) = data; f(buf, (int)sz); } } @@ -1001,8 +1000,8 @@ udp_server_handler(void *data) } struct fiber * -fiber_server(fiber_server_type type, int port, void (*handler)(void *data), void *data, - void (*on_bind)(void *data)) +fiber_server(fiber_server_type type, int port, void (*handler) (void *data), void *data, + void (*on_bind) (void *data)) { char *server_name; struct fiber_server *server; @@ -1010,7 +1009,8 @@ fiber_server(fiber_server_type type, int port, void (*handler)(void *data), void server_name = palloc(eter_pool, 64); snprintf(server_name, 64, "%i/acceptor", port); - s = fiber_create(server_name, -1, -1, (type == tcp_server) ? tcp_server_handler : udp_server_handler, data); + s = fiber_create(server_name, -1, -1, + (type == tcp_server) ? tcp_server_handler : udp_server_handler, data); s->data = server = palloc(eter_pool, sizeof(struct fiber_server)); assert(server != NULL); server->port = port; @@ -1042,8 +1042,7 @@ fiber_info(struct tbuf *out) struct frame *frame = fiber->rbp; tbuf_printf(out, " backtrace:\n"); - while (stack_bottom < (void *)frame && (void *)frame < stack_top) - { + while (stack_bottom < (void *)frame && (void *)frame < stack_top) { tbuf_printf(out, " - { frame: %p, pc: %p }\n", frame + 2 * sizeof(void *), frame->ret); frame = frame->rbp; @@ -1052,7 +1051,6 @@ fiber_info(struct tbuf *out) } } - void fiber_init(void) { @@ -1070,4 +1068,3 @@ fiber_init(void) fiber = &sched; last_used_fid = 100; } - diff --git a/core/iproto.c b/core/iproto.c index 05135e8fdcbafed7cb7ae0c54ce66d12ed6c2fb5..820a8aa4125efb09fd6e989a9ad0958d1c989b33 100644 --- a/core/iproto.c +++ b/core/iproto.c @@ -50,7 +50,7 @@ iproto_parse(struct tbuf *in) void iproto_interact(void *data) { - uint32_t(*callback) (uint32_t msg, struct tbuf *requst_data) = data; + uint32_t (*callback) (uint32_t msg, struct tbuf *requst_data) = data; struct tbuf *request; struct iproto_header_retcode *reply; ssize_t r; diff --git a/core/log_io.c b/core/log_io.c index e3f15ae98e5b13c30cf5dd6019ca13f28d2c7e39..4b557d80119b805917a2dba4e7c9078cec8a1e07 100644 --- a/core/log_io.c +++ b/core/log_io.c @@ -39,28 +39,34 @@ #include <fiber.h> #include <log_io.h> -#include "log_io_internal.h" #include <palloc.h> #include <say.h> #include <third_party/crc32.h> #include <util.h> #include <pickle.h> +#include <tbuf.h> +const u16 snap_tag = -1; +const u16 wal_tag = -2; +const u64 default_cookie = 0; +const u32 default_version = 11; const u32 snap_marker_v04 = -1U; const u64 xlog_marker_v04 = -1ULL; const u64 xlog_eof_marker_v04 = 0; -const u32 marker_v05 = 0xba0babed; -const u32 marker_v05_eof = 0x10adab1e; +const u32 marker_v11 = 0xba0babed; +const u32 eof_marker_v11 = 0x10adab1e; const char *snap_suffix = ".snap"; const char *xlog_suffix = ".xlog"; const char *v04 = "0.04\n"; const char *v03 = "0.03\n"; +const char *v11 = "0.11\n"; const char *snap_mark = "SNAP\n"; const char *xlog_mark = "XLOG\n"; #define ROW_EOF (void *)1 -static struct tbuf *row_reader_v04(FILE * f, struct palloc_pool *pool); +static struct tbuf *row_reader_v04(FILE *f, struct palloc_pool *pool); +static struct tbuf *row_reader_v11(FILE *f, struct palloc_pool *pool); struct log_io_iter { struct tarantool_coro coro; @@ -72,18 +78,18 @@ struct log_io_iter { int io_rate_limit; }; +struct row_v04 { + i64 lsn; /* this used to be tid */ + u16 type; + u32 len; + u8 data[]; +} __packed__; -i64 -confirmed_lsn(struct recovery_state *r) +static inline struct row_v04 *row_v04(const struct tbuf *t) { - return r->confirmed_lsn; + return (struct row_v04 *)t->data; } -struct child * -wal_writer(struct recovery_state *r) -{ - return r->wal_writer; -} int confirm_lsn(struct recovery_state *r, i64 lsn) @@ -92,14 +98,13 @@ confirm_lsn(struct recovery_state *r, i64 lsn) if (r->confirmed_lsn < lsn) { if (r->confirmed_lsn + 1 != lsn) - say_warn("non consecutive lsn, last confirmed:%"PRIi64 - " new:%"PRIi64" diff: %"PRIi64, + say_warn("non consecutive lsn, last confirmed:%" PRIi64 + " new:%" PRIi64 " diff: %" PRIi64, r->confirmed_lsn, lsn, lsn - r->confirmed_lsn); r->confirmed_lsn = lsn; return 0; } else { - say_warn("lsn double confirmed:%"PRIi64, - r->confirmed_lsn); + say_warn("lsn double confirmed:%" PRIi64, r->confirmed_lsn); } return -1; @@ -113,43 +118,89 @@ next_lsn(struct recovery_state *r, i64 new_lsn) else r->lsn = new_lsn; + say_debug("next_lsn(%p, %" PRIi64 ") => %" PRIi64, r, new_lsn, r->lsn); return r->lsn; } -static void -snap_class(struct log_io_class *c) -{ - c->suffix = snap_suffix; - c->filetype = snap_mark; - c->version = v03; - c->eof_marker_size = 0; /* no end marker */ - c->marker = snap_marker_v04; - c->marker_size = sizeof(snap_marker_v04); - c->rows_per_file = 0; -} - -static i64 row_v04_lsn(const struct tbuf *t) -{ - return row_v04(t)->lsn; -} - static void xlog04_class(struct log_io_class *c) { c->suffix = xlog_suffix; c->filetype = xlog_mark; c->version = v04; - c->row_lsn = row_v04_lsn; c->reader = row_reader_v04; c->marker = xlog_marker_v04; c->marker_size = sizeof(xlog_marker_v04); c->eof_marker = xlog_eof_marker_v04; c->eof_marker_size = sizeof(xlog_eof_marker_v04); - c->rows_per_file = 50000; /* sane defaults */ + c->rows_per_file = 50000; /* sane defaults */ c->fsync_delay = 0; } +static void +v11_class(struct log_io_class *c) +{ + c->suffix = xlog_suffix; + c->filetype = xlog_mark; + c->version = v11; + c->reader = row_reader_v11; + c->marker = marker_v11; + c->marker_size = sizeof(marker_v11); + c->eof_marker = eof_marker_v11; + c->eof_marker_size = sizeof(eof_marker_v11); + + c->fsync_delay = 0; +} + +static struct log_io_class ** +snap_classes(row_reader snap_row_reader, const char *dirname) +{ + struct log_io_class **c = calloc(3, sizeof(*c)); + if (c == NULL) + panic("calloc"); + + c[0] = calloc(1, sizeof(**c)); + c[1] = calloc(1, sizeof(**c)); + if (c[0] == NULL || c[1] == NULL) + panic("calloc"); + + c[0]->suffix = snap_suffix; + c[0]->filetype = snap_mark; + c[0]->version = v03; + c[0]->eof_marker_size = 0; /* no end marker */ + c[0]->marker = snap_marker_v04; + c[0]->marker_size = sizeof(snap_marker_v04); + c[0]->rows_per_file = 0; + c[0]->reader = snap_row_reader; + + v11_class(c[1]); + c[1]->filetype = c[0]->filetype; + c[1]->suffix = c[0]->suffix; + + c[0]->dirname = c[1]->dirname = dirname; + return c; +} + +static struct log_io_class ** +xlog_classes(const char *dirname) +{ + struct log_io_class **c = calloc(3, sizeof(*c)); + if (c == NULL) + panic("calloc"); + + c[0] = calloc(1, sizeof(**c)); + c[1] = calloc(1, sizeof(**c)); + if (c[0] == NULL || c[1] == NULL) + panic("calloc"); + + xlog04_class(c[0]); + v11_class(c[1]); + + c[0]->dirname = c[1]->dirname = dirname; + return c; +} + static void * iter_inner(struct log_io_iter *i, void *data) { @@ -172,7 +223,6 @@ close_iter(struct log_io_iter *i) tarantool_coro_destroy(&i->coro); } - static void read_rows(struct log_io_iter *i) { @@ -189,32 +239,37 @@ read_rows(struct log_io_iter *i) l->class->marker, l->class->marker_size); good_offset = ftello(l->f); -restart: + restart: if (marker_offset > 0) fseeko(l->f, marker_offset + 1, SEEK_SET); for (;;) { - say_debug("read_rows: loop start offt %" PRI_OFFT, ftello(l->f)); + say_debug("read_rows: loop start offt 0x%08" PRI_XFFT, ftello(l->f)); if (fread(&magic, l->class->marker_size, 1, l->f) != 1) goto eof; while ((magic & marker_mask) != l->class->marker) { int c = fgetc(l->f); - if (c == EOF) + if (c == EOF) { + say_debug("eof while looking for magic"); goto eof; - magic = (magic << 8) + (c & 0xff); + } + magic >>= 8; + magic |= (((u64)c & 0xff) << ((l->class->marker_size - 1) * 8)); } marker_offset = ftello(l->f) - l->class->marker_size; if (good_offset != marker_offset) - say_warn("skipped %"PRI_OFFT" bytes after %"PRI_OFFT" offset", + say_warn("skipped %" PRI_OFFT " bytes after 0x%08" PRI_XFFT " offset", marker_offset - good_offset, good_offset); - say_debug("magic found at %" PRI_OFFT, marker_offset); + say_debug("magic found at 0x%08" PRI_XFFT, marker_offset); row = l->class->reader(l->f, fiber->pool); if (row == ROW_EOF) goto eof; if (row == NULL) { + if (l->class->panic_if_error) + panic("failed to read row"); say_warn("failed to read row"); goto restart; } @@ -226,15 +281,12 @@ read_rows(struct log_io_iter *i) goto out; } - if (row_count % 1000 == 0) - prelease(fiber->pool); + prelease_after(fiber->pool, 128 * 1024); - if (++row_count % 100000 == 0) { - ev_now_update(); + if (++row_count % 100000 == 0) say_info("%.1fM rows processed", row_count / 1000000.); - } } -eof: + eof: /* * then only two cases of fully read file: * 1. eof_marker_size > 0 and it is the last record in file @@ -259,7 +311,7 @@ read_rows(struct log_io_iter *i) goto out; } -out: + out: l->rows += row_count; fseeko(l->f, good_offset, SEEK_SET); /* seek back to last known good offset */ @@ -274,23 +326,24 @@ read_rows(struct log_io_iter *i) } static void -iter_open(struct log_io *l, struct log_io_iter *i, void (*iterator)(struct log_io_iter *i)) +iter_open(struct log_io *l, struct log_io_iter *i, void (*iterator) (struct log_io_iter * i)) { memset(i, 0, sizeof(*i)); i->log = l; tarantool_coro_create(&i->coro, (void *)iterator, i); } - static int cmp_i64(const void *_a, const void *_b) { const i64 *a = _a, *b = _b; - return *a - *b; + if (*a == *b) + return 0; + return (*a > *b) ? 1 : -1; } static ssize_t -scan_dir(struct log_io_class *class, i64 ** ret_lsn) +scan_dir(struct log_io_class *class, i64 **ret_lsn) { DIR *dh = NULL; struct dirent *dent; @@ -323,8 +376,11 @@ scan_dir(struct log_io_class *class, i64 ** ret_lsn) continue; } - if (lsn[i] == LLONG_MAX || lsn[i] == LLONG_MIN) - goto out; + if (lsn[i] == LLONG_MAX || lsn[i] == LLONG_MIN) { + say_warn("can't parse `%s', skipping", dent->d_name); + continue; + } + i++; if (i == size) { i64 *n = palloc(fiber->pool, sizeof(i64) * size * 2); @@ -340,7 +396,7 @@ scan_dir(struct log_io_class *class, i64 ** ret_lsn) *ret_lsn = lsn; result = i; -out: + out: if (errno != 0) say_syserror("error reading directory `%s'", class->dirname); @@ -385,13 +441,28 @@ find_including_file(struct log_io_class *class, i64 target_lsn) * is not known beforehand. so, we simply return the last one. */ -out: + out: return *lsn; } +struct tbuf * +convert_to_v11(struct tbuf *orig, u16 tag, const u64 cookie, i64 lsn) +{ + struct tbuf *row = tbuf_alloc(orig->pool); + tbuf_ensure(row, sizeof(struct row_v11)); + row->len = sizeof(struct row_v11); + row_v11(row)->lsn = lsn; + row_v11(row)->tm = 0; + row_v11(row)->len = orig->len + sizeof(tag) + sizeof(cookie); + + tbuf_append(row, &tag, sizeof(tag)); + tbuf_append(row, &cookie, sizeof(cookie)); + tbuf_append(row, orig->data, orig->len); + return row; +} static struct tbuf * -row_reader_v04(FILE * f, struct palloc_pool *pool) +row_reader_v04(FILE *f, struct palloc_pool *pool) { const int header_size = offsetof(struct row_v04, data); struct tbuf *m = tbuf_alloc(pool); @@ -428,7 +499,51 @@ row_reader_v04(FILE * f, struct palloc_pool *pool) return NULL; } - say_debug("read row success lsn:%" PRIi64, row_v04(m)->lsn); + say_debug("read row v04 success lsn:%" PRIi64, row_v04(m)->lsn); + + /* we're copying row data twice here, it's ok since this is legacy function */ + struct tbuf *data = tbuf_alloc(pool); + tbuf_append(data, &row_v04(m)->type, sizeof(row_v04(m)->type)); + tbuf_append(data, row_v04(m)->data, row_v04(m)->len); + + return convert_to_v11(data, wal_tag, default_cookie, row_v04(m)->lsn); +} + +static struct tbuf * +row_reader_v11(FILE *f, struct palloc_pool *pool) +{ + struct tbuf *m = tbuf_alloc(pool); + + u32 header_crc, data_crc; + + tbuf_ensure(m, sizeof(struct row_v11)); + if (fread(m->data, sizeof(struct row_v11), 1, f) != 1) + return ROW_EOF; + + m->len = offsetof(struct row_v11, data); + + /* header crc32c calculated on <lsn, tm, len, data_crc32c> */ + header_crc = crc32c(0, m->data + offsetof(struct row_v11, lsn), + sizeof(struct row_v11) - offsetof(struct row_v11, lsn)); + + if (row_v11(m)->header_crc32c != header_crc) { + say_error("header crc32c mismatch"); + return NULL; + } + + tbuf_ensure(m, m->len + row_v11(m)->len); + if (fread(row_v11(m)->data, row_v11(m)->len, 1, f) != 1) + return ROW_EOF; + + m->len += row_v11(m)->len; + + data_crc = crc32c(0, row_v11(m)->data, row_v11(m)->len); + if (row_v11(m)->data_crc32c != data_crc) { + say_error("data crc32c mismatch"); + return NULL; + } + + say_debug("read row v11 success lsn:%" PRIi64, row_v11(m)->lsn); return m; } @@ -456,22 +571,9 @@ close_log(struct log_io **lptr) static int flush_log(struct log_io *l) { - - static double last = 0; - double now; - struct timeval t; - if (fflush(l->f) < 0) return -1; - if (gettimeofday(&t, NULL) < 0) { - say_syserror("gettimeofday"); - return -1; - } - now = t.tv_sec + t.tv_usec / 1000000.; - - if (l->class->fsync_delay == 0 || now - last < l->class->fsync_delay) - return 0; #ifdef Linux if (fdatasync(fileno(l->f)) < 0) { say_syserror("fdatasync"); @@ -483,7 +585,6 @@ flush_log(struct log_io *l) return -1; } #endif - last = now; return 0; } @@ -499,12 +600,15 @@ write_header(struct log_io *l) if (fwrite(l->class->version, strlen(l->class->version), 1, l->f) != 1) return -1; - time(&tm); - ctime_r(&tm, buf); - /* 20 bytes is hardcoded timestring length in silverspoon */ - //buf[19] = '\n'; - if (fwrite(buf, strlen(buf), 1, l->f) != 1) - return -1; + if (strcmp(l->class->version, v11) == 0) { + if (fwrite("\n", 1, 1, l->f) != 1) + return -1; + } else { + time(&tm); + ctime_r(&tm, buf); + if (fwrite(buf, strlen(buf), 1, l->f) != 1) + return -1; + } return 0; } @@ -518,7 +622,8 @@ format_filename(char *filename, struct log_io_class *class, i64 lsn, int suffix) filename = buf; switch (suffix) { - case 0: snprintf(filename, PATH_MAX, "%s/%020" PRIi64 "%s", + case 0: + snprintf(filename, PATH_MAX, "%s/%020" PRIi64 "%s", class->dirname, lsn, class->suffix); break; case -1: @@ -534,9 +639,10 @@ format_filename(char *filename, struct log_io_class *class, i64 lsn, int suffix) } static struct log_io * -open_for_read(struct recovery_state *recover, struct log_io_class *class, i64 lsn, int suffix, const char *filename) +open_for_read(struct recovery_state *recover, struct log_io_class **class, i64 lsn, int suffix, + const char *filename) { - char filetype[32], version[32], buf[32]; + char filetype[32], version[32], buf[256]; struct log_io *l = NULL; char *r; char *error = "unknown error"; @@ -546,13 +652,12 @@ open_for_read(struct recovery_state *recover, struct log_io_class *class, i64 ls goto error; memset(l, 0, sizeof(*l)); l->mode = LOG_READ; - l->class = class; l->stat.data = recover; /* when filename is not null it is forced open for debug reading */ if (filename == NULL) { assert(lsn != 0); - format_filename(l->filename, class, lsn, suffix); + format_filename(l->filename, *class, lsn, suffix); } else { assert(lsn == 0); strncpy(l->filename, filename, PATH_MAX); @@ -578,24 +683,43 @@ open_for_read(struct recovery_state *recover, struct log_io_class *class, i64 ls goto error; } - if (strcmp(class->filetype, filetype) != 0) { + if (strcmp((*class)->filetype, filetype) != 0) { error = "unknown filetype"; goto error; } - if (strcmp(class->version, version) != 0) { - error = "unknown version"; - goto error; + while (*class) { + if (strcmp((*class)->version, version) == 0) + break; + class++; } - r = fgets(buf, sizeof(buf), l->f); /* skip line with time */ - if (r == NULL) { - error = "header reading failed"; + if (*class == NULL) { + error = "unknown version"; goto error; } + l->class = *class; + + if (strcmp(version, v11) == 0) { + for (;;) { + r = fgets(buf, sizeof(buf), l->f); + if (r == NULL) { + error = "header reading failed"; + goto error; + } + if (strcmp(r, "\n") == 0 || strcmp(r, "\r\n") == 0) + break; + } + } else { + r = fgets(buf, sizeof(buf), l->f); /* skip line with time */ + if (r == NULL) { + error = "header reading failed"; + goto error; + } + } return l; -error: + error: say_error("open_for_read: failed to open `%s': %s", l->filename, error); if (l != NULL) { if (l->f != NULL) @@ -625,7 +749,7 @@ open_for_write(struct recovery_state *recover, struct log_io_class *class, i64 l format_filename(l->filename, class, lsn, suffix); say_debug("find_log for writing `%s'", l->filename); - fd = open(l->filename, O_WRONLY|O_CREAT|O_EXCL|O_APPEND, 0664); + fd = open(l->filename, O_WRONLY | O_CREAT | O_EXCL | O_APPEND, 0664); if (fd < 0) { error = strerror(errno); goto error; @@ -640,7 +764,7 @@ open_for_write(struct recovery_state *recover, struct log_io_class *class, i64 l say_info("creating `%s'", l->filename); write_header(l); return l; -error: + error: say_error("find_log: failed to open `%s': %s", l->filename, error); if (l != NULL) { if (l->f != NULL) @@ -653,39 +777,32 @@ open_for_write(struct recovery_state *recover, struct log_io_class *class, i64 l /* this little hole shouldn't be used too much */ int read_log(const char *filename, row_reader reader, - row_handler xlog_handler, row_handler snap_handler, void *state) + row_handler *xlog_handler, row_handler *snap_handler, void *state) { struct log_io_iter i; struct log_io *l; - struct log_io_class class; + struct log_io_class **c; struct tbuf *row; - - memset(&i, 0, sizeof(i)); - memset(&class, 0, sizeof(class)); + row_handler *h; if (strstr(filename, xlog_suffix)) { - class.handler = xlog_handler; - xlog04_class(&class); - } - if (strstr(filename, snap_suffix)) { - class.handler = snap_handler; - snap_class(&class); - if (reader) - class.reader = reader; - } - - if (class.filetype == NULL) { + c = xlog_classes(NULL); + h = xlog_handler; + } else if (strstr(filename, snap_suffix)) { + c = snap_classes(reader, NULL); + h = snap_handler; + } else { say_error("don't know what how to read `%s'", filename); return -1; } - l = open_for_read(NULL, &class, 0, 0, filename); + l = open_for_read(NULL, c, 0, 0, filename); iter_open(l, &i, read_rows); while ((row = iter_inner(&i, (void *)1))) - class.handler(state, row); + h(state, row); if (i.error != 0) - say_error("log `%s' wasn't correctly closed", filename); + say_error("binary log `%s' wasn't correctly closed", filename); close_iter(&i); return i.error; @@ -707,14 +824,14 @@ recover_snap(struct recovery_state *r) goto out; } - lsn = greatest_lsn(&r->snap_class); + lsn = greatest_lsn(r->snap_prefered_class); if (lsn <= 0) { say_error("can't find snapshot"); goto out; } - snap = open_for_read(r, &r->snap_class, lsn, 0, NULL); + snap = open_for_read(r, r->snap_class, lsn, 0, NULL); if (snap == NULL) { say_error("can't find/open snapshot"); goto out; @@ -724,7 +841,7 @@ recover_snap(struct recovery_state *r) say_info("recover from `%s'", snap->filename); while ((row = iter_inner(&i, (void *)1))) { - if (snap->class->handler(r, row) < 0) { + if (r->row_handler(r, row) < 0) { result = -1; goto out; } @@ -732,7 +849,7 @@ recover_snap(struct recovery_state *r) result = i.error; if (result == 0) r->lsn = r->confirmed_lsn = lsn; -out: + out: if (result != 0) say_error("failure reading snapshot"); @@ -759,31 +876,38 @@ static int recover_wal(struct recovery_state *r, struct log_io *l) { struct log_io_iter i; - struct tbuf *row; + struct tbuf *row = NULL; int result; + if (setjmp(fiber->exc) != 0) { + result = -1; + goto out; + } + memset(&i, 0, sizeof(i)); iter_open(l, &i, read_rows); while ((row = iter_inner(&i, (void *)1))) { - if (r && l->class->row_lsn(row) <= confirmed_lsn(r)) { + i64 lsn = row_v11(row)->lsn; + if (r && lsn <= r->confirmed_lsn) { say_debug("skipping too young row"); continue; } - if (l->class->handler(r, row) < 0) { + /* after handler(r, row) returned, row may be modified, do not use it */ + if (r->row_handler(r, row) < 0) { say_error("row_handler returned error"); result = -1; goto out; } if (r) { - next_lsn(r, l->class->row_lsn(row)); - confirm_lsn(r, l->class->row_lsn(row)); + next_lsn(r, lsn); + confirm_lsn(r, lsn); } } result = i.error; -out: + out: /* * since we don't close log_io * we must rewind log_io to last known @@ -818,27 +942,29 @@ recover_remaining_wals(struct recovery_state *r) i64 current_lsn, wal_greatest_lsn; size_t rows_before; - current_lsn = confirmed_lsn(r) + 1; - wal_greatest_lsn = greatest_lsn(&r->wal_class); + current_lsn = r->confirmed_lsn + 1; + wal_greatest_lsn = greatest_lsn(r->wal_prefered_class); /* if the caller already opened WAL for us, recover from it first */ if (r->current_wal != NULL) goto recover_current_wal; - while (confirmed_lsn(r) < wal_greatest_lsn) { + while (r->confirmed_lsn < wal_greatest_lsn) { /* if newer WAL appeared in directory before current_wal was fully read try reread last */ if (r->current_wal != NULL) { if (r->current_wal->retry++ < 3) { - say_warn("try reread `%s' despite newer WAL exists", r->current_wal->filename); + say_warn("try reread `%s' despite newer WAL exists", + r->current_wal->filename); goto recover_current_wal; } else { - say_warn("wal `%s' wasn't correctly closed", r->current_wal->filename); + say_warn("wal `%s' wasn't correctly closed", + r->current_wal->filename); close_log(&r->current_wal); } } - current_lsn = confirmed_lsn(r) + 1; /* TODO: find better way looking for next xlog */ - next_wal = open_for_read(r, &r->wal_class, current_lsn, suffix, NULL); + current_lsn = r->confirmed_lsn + 1; /* TODO: find better way looking for next xlog */ + next_wal = open_for_read(r, r->wal_class, current_lsn, suffix, NULL); if (next_wal == NULL) { if (suffix++ < 10) continue; @@ -849,7 +975,7 @@ recover_remaining_wals(struct recovery_state *r) r->current_wal = next_wal; say_info("recover from `%s'", r->current_wal->filename); - recover_current_wal: + recover_current_wal: rows_before = r->current_wal->rows; result = recover_wal(r, r->current_wal); if (result < 0) { @@ -869,11 +995,12 @@ recover_remaining_wals(struct recovery_state *r) if (suffix++ < 10) continue; - say_error("too many filename confilcters"); + say_error("too many filename conflicters"); result = -1; break; } else { - name = format_filename(NULL, &r->wal_class, current_lsn, suffix + 1); + name = format_filename(NULL, r->wal_prefered_class, + current_lsn, suffix + 1); if (access(name, F_OK) == 0) { say_error("found conflicter `%s' after successful reading", name); result = -1; @@ -882,7 +1009,8 @@ recover_remaining_wals(struct recovery_state *r) } if (result == LOG_EOF) { - say_info("done `%s' confirmed_lsn:%"PRIi64, r->current_wal->filename, confirmed_lsn(r)); + say_info("done `%s' confirmed_lsn:%" PRIi64, r->current_wal->filename, + r->confirmed_lsn); close_log(&r->current_wal); } suffix = 0; @@ -892,7 +1020,7 @@ recover_remaining_wals(struct recovery_state *r) * it's not a fatal error then last wal is empty * but if we lost some logs it is fatal error */ - if (wal_greatest_lsn > confirmed_lsn(r) + 1) { + if (wal_greatest_lsn > r->confirmed_lsn + 1) { say_error("not all wals have been successfuly read"); result = -1; } @@ -915,13 +1043,13 @@ recover(struct recovery_state *r, i64 lsn) if (lsn == 0) { result = recover_snap(r); if (result < 0) { - if (greatest_lsn(&r->snap_class) <= 0) { + if (greatest_lsn(r->snap_prefered_class) <= 0) { say_crit("don't you forget to initialize storage with --init_storage switch?"); _exit(1); } panic("snapshot recovery failed"); } - say_info("snapshot recovered, confirmed lsn:%"PRIi64, confirmed_lsn(r)); + say_info("snapshot recovered, confirmed lsn:%" PRIi64, r->confirmed_lsn); } else { /* * note, that recovery start with lsn _NEXT_ to confirmed one @@ -934,14 +1062,14 @@ recover(struct recovery_state *r, i64 lsn) * so find wal which contains record with next lsn */ if (r->current_wal == NULL) { - i64 next_lsn = confirmed_lsn(r) + 1; - i64 lsn = find_including_file(&r->wal_class, next_lsn); + i64 next_lsn = r->confirmed_lsn + 1; + i64 lsn = find_including_file(r->wal_prefered_class, next_lsn); if (lsn <= 0) { - say_error("can't find wal containing record with lsn:%"PRIi64, next_lsn); + say_error("can't find wal containing record with lsn:%" PRIi64, next_lsn); result = -1; goto out; } else { - r->current_wal = open_for_read(r, &r->wal_class, lsn, 0, NULL); + r->current_wal = open_for_read(r, r->wal_class, lsn, 0, NULL); if (r->current_wal == NULL) { result = -1; goto out; @@ -950,17 +1078,18 @@ recover(struct recovery_state *r, i64 lsn) } result = recover_remaining_wals(r); - say_info("wals recovered, confirmed lsn: %"PRIi64, confirmed_lsn(r)); -out: + if (result < 0) + panic("recover failed"); + say_info("wals recovered, confirmed lsn: %" PRIi64, r->confirmed_lsn); + out: prelease(fiber->pool); return result; } - -static void recover_follow_file(ev_stat * w, int revents __unused__); +static void recover_follow_file(ev_stat *w, int revents __unused__); static void -recover_follow_dir(ev_timer * w, int revents __unused__) +recover_follow_dir(ev_timer *w, int revents __unused__) { struct recovery_state *r = w->data; struct log_io *wal = r->current_wal; @@ -977,15 +1106,16 @@ recover_follow_dir(ev_timer * w, int revents __unused__) } static void -recover_follow_file(ev_stat * w, int revents __unused__) +recover_follow_file(ev_stat *w, int revents __unused__) { struct recovery_state *r = w->data; int result; result = recover_wal(r, r->current_wal); if (result < 0) - panic("recover failed: %i", result); + panic("recover failed"); if (result == LOG_EOF) { - say_info("done `%s' confirmed_lsn:%"PRIi64, r->current_wal->filename, confirmed_lsn(r)); + say_info("done `%s' confirmed_lsn:%" PRIi64, r->current_wal->filename, + r->confirmed_lsn); close_log(&r->current_wal); recover_follow_dir((ev_timer *)w, 0); } @@ -994,9 +1124,9 @@ recover_follow_file(ev_stat * w, int revents __unused__) void recover_follow(struct recovery_state *r, ev_tstamp wal_dir_rescan_delay) { - ev_timer_init(&r->wal_class.timer, recover_follow_dir, + ev_timer_init(&r->wal_timer, recover_follow_dir, wal_dir_rescan_delay, wal_dir_rescan_delay); - ev_timer_start(&r->wal_class.timer); + ev_timer_start(&r->wal_timer); if (r->current_wal != NULL) { ev_stat *stat = &r->current_wal->stat; ev_stat_init(stat, recover_follow_file, r->current_wal->filename, 0.); @@ -1007,16 +1137,24 @@ recover_follow(struct recovery_state *r, ev_tstamp wal_dir_rescan_delay) void recover_finalize(struct recovery_state *r) { - if (ev_is_active(&r->wal_class.timer)) - ev_timer_stop(&r->wal_class.timer); + int result; + + if (ev_is_active(&r->wal_timer)) + ev_timer_stop(&r->wal_timer); if (r->current_wal != NULL) { if (ev_is_active(&r->current_wal->stat)) ev_stat_stop(&r->current_wal->stat); } - if (recover_remaining_wals(r) < 0) + result = recover_remaining_wals(r); + if (result < 0) panic("unable to scucessfully finalize recovery"); + + if (r->current_wal != NULL && result != LOG_EOF) { + say_warn("wal `%s' wasn't correctly closed", r->current_wal->filename); + close_log(&r->current_wal); + } } static struct wal_write_request * @@ -1026,17 +1164,19 @@ wal_write_request(const struct tbuf *t) } static struct tbuf * -write_to_disk_v04(void *_state, struct tbuf *t) +write_to_disk(void *_state, struct tbuf *t) { static struct log_io *wal = NULL, *wal_to_close = NULL; + static ev_tstamp last_flush = 0; static size_t rows = 0; - struct tbuf *reply; - u32 calculated_crc; + struct tbuf *reply, *header; struct recovery_state *r = _state; u32 result = 0; int suffix = 0; + /* we're not running inside ev_loop, so update ev_now manually */ ev_now_update(); + /* caller requested termination */ if (t == NULL) { if (wal != NULL) @@ -1048,7 +1188,7 @@ write_to_disk_v04(void *_state, struct tbuf *t) /* if there is filename conflict, try filename with lager suffix */ while (wal == NULL && suffix < 10) { - wal = open_for_write(r, &r->wal_class, wal_write_request(t)->lsn, suffix); + wal = open_for_write(r, r->wal_prefered_class, wal_write_request(t)->lsn, suffix); suffix++; } if (wal_to_close != NULL) { @@ -1063,26 +1203,47 @@ write_to_disk_v04(void *_state, struct tbuf *t) say_syserror("can't write marker to wal"); goto fail; } - if (fwrite(wal_write_request(t)->data, wal_write_request(t)->len, 1, wal->f) != 1) { - say_syserror("can't write data to wal"); + + header = tbuf_alloc(t->pool); + tbuf_ensure(header, sizeof(struct row_v11)); + header->len = sizeof(struct row_v11); + + row_v11(header)->lsn = wal_write_request(t)->lsn; + row_v11(header)->tm = ev_now(); + row_v11(header)->len = wal_write_request(t)->len; + row_v11(header)->data_crc32c = + crc32c(0, wal_write_request(t)->data, wal_write_request(t)->len); + row_v11(header)->header_crc32c = + crc32c(0, header->data + field_sizeof(struct row_v11, header_crc32c), + sizeof(struct row_v11) - field_sizeof(struct row_v11, header_crc32c)); + + if (fwrite(header->data, header->len, 1, wal->f) != 1) { + say_syserror("can't write row header to wal"); goto fail; } - calculated_crc = crc32(wal_write_request(t)->data, wal_write_request(t)->len); - if (fwrite(&calculated_crc, sizeof(calculated_crc), 1, wal->f) != 1) { - say_syserror("can't write crc to wal"); + if (fwrite(wal_write_request(t)->data, wal_write_request(t)->len, 1, wal->f) != 1) { + say_syserror("can't write row data to wal"); goto fail; } - if (flush_log(wal) < 0) { + /* flush stdio buffer to keep feeder in sync */ + if (fflush(wal->f) < 0) { say_syserror("can't flush wal"); goto fail; } + if (wal->class->fsync_delay > 0 && ev_now() - last_flush >= wal->class->fsync_delay) { + if (flush_log(wal) < 0) { + say_syserror("can't flush wal"); + goto fail; + } + last_flush = ev_now(); + } + rows++; if (wal->class->rows_per_file <= rows || - (wal_write_request(t)->lsn + 1) % wal->class->rows_per_file == 0 ) - { + (wal_write_request(t)->lsn + 1) % wal->class->rows_per_file == 0) { wal_to_close = wal; wal = NULL; rows = 0; @@ -1092,23 +1253,26 @@ write_to_disk_v04(void *_state, struct tbuf *t) tbuf_append(reply, &result, sizeof(result)); return reply; -fail: + fail: result = 1; tbuf_append(reply, &result, sizeof(result)); return reply; } bool -wal_write(struct recovery_state *r, i64 lsn, struct tbuf *data) +wal_write(struct recovery_state *r, u16 tag, u64 cookie, i64 lsn, struct tbuf *row) { - struct tbuf *m = tbuf_alloc(data->pool); + struct tbuf *m = tbuf_alloc(row->pool); struct msg *a; - say_debug("wal_write lsn=%"PRIi64, lsn); - tbuf_reserve(m, sizeof(struct wal_write_request) + data->len); + say_debug("wal_write lsn=%" PRIi64, lsn); + tbuf_reserve(m, sizeof(struct wal_write_request) + sizeof(tag) + sizeof(cookie) + row->len); + m->len = sizeof(struct wal_write_request); wal_write_request(m)->lsn = lsn; - wal_write_request(m)->len = data->len; - memcpy(wal_write_request(m)->data, data->data, data->len); + wal_write_request(m)->len = row->len + sizeof(tag) + sizeof(cookie); + tbuf_append(m, &tag, sizeof(tag)); + tbuf_append(m, &cookie, sizeof(cookie)); + tbuf_append(m, row->data, row->len); if (write_inbox(r->wal_writer->out, m) == false) { say_warn("wal writer inbox is full"); @@ -1117,119 +1281,130 @@ wal_write(struct recovery_state *r, i64 lsn, struct tbuf *data) a = read_inbox(); u32 reply = read_u32(a->msg); - say_debug("wal_write reply=%"PRIu32, reply); + say_debug("wal_write reply=%" PRIu32, reply); if (reply != 0) say_warn("wal writer returned error status"); return reply == 0; } -bool -wal_write_v04(struct recovery_state * r, int op, const u8 *data, size_t len) -{ - i64 lsn = next_lsn(r, 0); - struct tbuf *m = tbuf_alloc(fiber->pool); - tbuf_reserve(m, sizeof(struct row_v04) + len); - row_v04(m)->lsn = lsn; - row_v04(m)->type = op; - row_v04(m)->len = len; - memcpy(row_v04(m)->data, data, row_v04(m)->len); - - if (wal_write(r, lsn, m)) { - confirm_lsn(r, lsn); - return true; - } - - say_warn("wal_write failed, txn lsn:%"PRIi64 " aborted", lsn); - return false; -} - struct recovery_state * recover_init(const char *snap_dirname, const char *wal_dirname, - row_reader snap_row_reader, row_handler snap_row_handler, row_handler wal_row_handler, - int rows_per_file, double fsync_delay, double snap_io_rate_limit, + row_reader snap_row_reader, row_handler row_handler, + int rows_per_file, double fsync_delay, int inbox_size, int flags, void *data) { - struct recovery_state *r = malloc(sizeof(*r)); /* let it leak */ - memset(r, 0, sizeof(*r)); + struct recovery_state *r = p0alloc(eter_pool, sizeof(*r)); - snap_class(&r->snap_class); - r->snap_class.dirname = snap_dirname; - r->snap_class.reader = snap_row_reader; - r->snap_class.handler = snap_row_handler; + r->wal_timer.data = r; + r->row_handler = row_handler; + r->data = data; - xlog04_class(&r->wal_class); - r->wal_class.dirname = wal_dirname; - r->wal_class.handler = wal_row_handler; - r->wal_class.timer.data = r; - r->wal_class.rows_per_file = rows_per_file; - r->wal_class.fsync_delay = fsync_delay; + r->snap_class = snap_classes(snap_row_reader, snap_dirname); + r->snap_prefered_class = r->snap_class[1]; + + r->wal_class = xlog_classes(wal_dirname); + r->wal_prefered_class = r->wal_class[1]; + r->wal_prefered_class->rows_per_file = rows_per_file; + r->wal_prefered_class->fsync_delay = fsync_delay; + + if ((flags & RECOVER_READONLY) == 0) + r->wal_writer = spawn_child("wal_writer", inbox_size, write_to_disk, r); - r->data = data; - if ((flags & RECOVER_READONLY) == 0) { - r->wal_writer = spawn_child("wal_writer", inbox_size, write_to_disk_v04, r); - r->snap_io_rate_limit = snap_io_rate_limit * 1024 * 1024; - } return r; } +void +recovery_setup_panic(struct recovery_state *r, bool on_snap_error, bool on_wal_error) +{ + struct log_io_class **class; + + for (class = r->wal_class; *class; class++) + (*class)->panic_if_error = on_wal_error; + + for (class = r->snap_class; *class; class++) + (*class)->panic_if_error = on_snap_error; +} static void write_rows(struct log_io_iter *i) { struct log_io *l = i->log; - struct tbuf *row; + struct tbuf *row, *data; + + row = tbuf_alloc(eter_pool); + tbuf_ensure(row, sizeof(struct row_v11)); + row->len = sizeof(struct row_v11); goto start; for (;;) { coro_transfer(&i->coro.ctx, &fiber->coro.ctx); - start: - row = i->to; + start: + data = i->to; if (fwrite(&l->class->marker, l->class->marker_size, 1, l->f) != 1) panic("fwrite"); + row_v11(row)->lsn = 0; /* unused */ + row_v11(row)->tm = ev_now(); + row_v11(row)->len = data->len; + row_v11(row)->data_crc32c = crc32c(0, data->data, data->len); + row_v11(row)->header_crc32c = + crc32c(0, row->data + field_sizeof(struct row_v11, header_crc32c), + sizeof(struct row_v11) - field_sizeof(struct row_v11, + header_crc32c)); + if (fwrite(row->data, row->len, 1, l->f) != 1) panic("fwrite"); + + if (fwrite(data->data, data->len, 1, l->f) != 1) + panic("fwrite"); + + prelease_after(fiber->pool, 128 * 1024); } } - void -snapshot_write_row(struct log_io_iter *i, struct tbuf *row) +snapshot_write_row(struct log_io_iter *i, u16 tag, u64 cookie, struct tbuf *row) { static int rows; static int bytes; - static struct timeval last; + ev_tstamp elapsed; + static ev_tstamp last = 0; + struct tbuf *wal_row = tbuf_alloc(fiber->pool); + + tbuf_append(wal_row, &tag, sizeof(tag)); + tbuf_append(wal_row, &cookie, sizeof(cookie)); + tbuf_append(wal_row, row->data, row->len); - i->to = row; + i->to = wal_row; if (i->io_rate_limit > 0) { - if (last.tv_sec == 0) - gettimeofday(&last, NULL); - bytes += row->len; + if (last == 0) { + ev_now_update(); + last = ev_now(); + } - while (bytes >= i->io_rate_limit) { - struct timeval now; - useconds_t elapsed; + bytes += row->len + sizeof(struct row_v11); - gettimeofday(&now, NULL); - elapsed = (now.tv_sec - last.tv_sec) * 1000000 + now.tv_usec - last.tv_usec; + while (bytes >= i->io_rate_limit) { + flush_log(i->log); - if (elapsed < 1000000) - usleep(1000000 - elapsed); + ev_now_update(); + elapsed = ev_now() - last; + if (elapsed < 1) + usleep(((1 - elapsed) * 1000000)); - gettimeofday(&last, NULL); + ev_now_update(); + last = ev_now(); bytes -= i->io_rate_limit; } } coro_transfer(&fiber->coro.ctx, &i->coro.ctx); - if (++rows % 100000 == 0) { - ev_now_update(); + if (++rows % 100000 == 0) say_crit("%.1fM rows written", rows / 1000000.); - } } void -snapshot_save(struct recovery_state *r, void (*f)(struct log_io_iter *)) +snapshot_save(struct recovery_state *r, void (*f) (struct log_io_iter *)) { struct log_io_iter i; struct log_io *snap; @@ -1238,7 +1413,7 @@ snapshot_save(struct recovery_state *r, void (*f)(struct log_io_iter *)) memset(&i, 0, sizeof(i)); - snap = open_for_write(r, &r->snap_class, confirmed_lsn(r), -1); + snap = open_for_write(r, r->snap_prefered_class, r->confirmed_lsn, -1); if (snap == NULL) panic("can't open snap for writing"); @@ -1251,11 +1426,10 @@ snapshot_save(struct recovery_state *r, void (*f)(struct log_io_iter *)) dot = strrchr(final_filename, '.'); *dot = 0; - ev_now_update(); say_info("saving snapshot `%s'", final_filename); f(&i); - if (fsync(fileno(snap->f)) < 0) + if (fsync(fileno(snap->f)) < 0) panic("fsync"); if (rename(snap->filename, final_filename) != 0) @@ -1263,6 +1437,5 @@ snapshot_save(struct recovery_state *r, void (*f)(struct log_io_iter *)) close_log(&snap); - ev_now_update(); say_info("done"); } diff --git a/core/log_io_internal.h b/core/log_io_internal.h deleted file mode 100644 index 84090e2d841fc3947d4f444fe9fb2e9823195115..0000000000000000000000000000000000000000 --- a/core/log_io_internal.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2010 Mail.RU - * Copyright (C) 2010 Yuriy Vostrikov - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef TARANTOOL_LOG_IO_INTERNAL_H -#define TARANTOOL_LOG_IO_INTERNAL_H - -enum log_mode { - LOG_READ, - LOG_WRITE -}; - -struct log_io_class { - ev_timer timer; - - i64 (*row_lsn)(const struct tbuf *); - row_handler handler; - row_reader reader; - u64 marker, eof_marker; - size_t marker_size, eof_marker_size; - size_t rows_per_file; - double fsync_delay; - - const char *filetype; - const char *version; - const char *suffix; - const char *dirname; -}; - -struct log_io { - struct log_io_class *class; - FILE *f; - - ev_stat stat; - enum log_mode mode; - size_t rows; - size_t retry; - char filename[PATH_MAX + 1]; -}; - -struct recovery_state { - i64 lsn, confirmed_lsn; - - struct log_io *current_wal; /* the WAL we'r currently reading/writing from/to */ - struct log_io_class snap_class, wal_class; - struct child *wal_writer; - void *data; - int snap_io_rate_limit; -}; - -struct wal_write_request { - i64 lsn; - u32 len; - u8 data[]; -} __packed__; - -bool wal_write(struct recovery_state *r, i64 lsn, struct tbuf *data); - -#endif diff --git a/core/log_io_remote.c b/core/log_io_remote.c index e0134cd41a8cd577b8403c60fbaa2b505301e98f..fc8713702ef8ff31a3d0abf353f6a8565dd87910 100644 --- a/core/log_io_remote.c +++ b/core/log_io_remote.c @@ -36,103 +36,172 @@ #include <say.h> #include <log_io.h> -#include "log_io_internal.h" +#include <pickle.h> +struct remote_state { + struct recovery_state *r; + int (*handler) (struct recovery_state * r, struct tbuf *row); +}; + +static u32 +row_v11_len(struct tbuf *r) +{ + if (r->len < sizeof(struct row_v11)) + return 0; + + if (r->len < sizeof(struct row_v11) + row_v11(r)->len) + return 0; + + return sizeof(struct row_v11) + row_v11(r)->len; +} static struct tbuf * -row_reader_v04(struct palloc_pool *pool) +row_reader_v11() { - const int header_size = offsetof(struct row_v04, data); - struct tbuf *m = tbuf_alloc(pool); + const int header_size = sizeof(struct row_v11); + struct tbuf *m; - if (fiber_read(m->data, header_size) != header_size) { - say_error("unexpected eof reading row header"); - return NULL; - } - m->len = header_size; + for (;;) { + if (row_v11_len(fiber->rbuf) != 0) { + m = tbuf_split(fiber->rbuf, row_v11_len(fiber->rbuf)); + say_debug("read row bytes:%" PRIu32 " %s", m->len, tbuf_to_hex(m)); + return m; + } - tbuf_ensure(m, header_size + row_v04(m)->len); - if (fiber_read(row_v04(m)->data, row_v04(m)->len) != row_v04(m)->len) { - say_error("unexpected eof reading row body"); - return NULL; + if (fiber_bread(fiber->rbuf, header_size) <= 0) { + say_error("unexpected eof reading row header"); + return NULL; + } } - m->len += row_v04(m)->len; - - say_debug("read row bytes:%"PRIu32" %s", m->len, tbuf_to_hex(m)); - return m; } - -static void -pull_from_remote(void *state) +static struct tbuf * +remote_read_row(i64 initial_lsn) { - struct recovery_state *r = state; struct tbuf *row; - i64 lsn; - int rows = 0; bool warning_said = false; const int reconnect_delay = 1; + const char *err = NULL; + u32 version; for (;;) { if (fiber->fd < 0) { if (fiber_connect(fiber->data) < 0) { - if (!warning_said) { - say_syserror("can't connect to feeder"); - say_info("will retry every %i second", reconnect_delay); - warning_said = true; - } - fiber_sleep(reconnect_delay); - continue; + err = "can't connect to feeder"; + goto err; + } + + if (fiber_write(&initial_lsn, sizeof(initial_lsn)) != sizeof(initial_lsn)) { + err = "can't write version"; + goto err; } - say_crit("succefully connected to feeder"); - lsn = confirmed_lsn(r) + 1; - fiber_write(&lsn, sizeof(lsn)); + if (fiber_read(&version, sizeof(version)) != sizeof(version)) { + err = "can't read version"; + goto err; + } + + if (version != default_version) { + err = "remote version mismatch"; + goto err; + } - say_crit("starting remote recovery from lsn:%"PRIi64, lsn); + say_crit("succefully connected to feeder"); + say_crit("starting remote recovery from lsn:%" PRIi64, initial_lsn); warning_said = false; + err = NULL; } - row = row_reader_v04(fiber->pool); + row = row_reader_v11(fiber->pool); if (row == NULL) { - fiber_close(); - fiber_sleep(reconnect_delay); - continue; + err = "can't read row"; + goto err; } - if (r->wal_class.handler(r, row) < 0) - panic("replication failure: can't apply row"); + return row; - if (wal_write(r, r->wal_class.row_lsn(row), row) == false) - panic("replication failure: can't write row to WAL"); + err: + if (err != NULL && !warning_said) { + say_info("%s", err); + say_info("will retry every %i second", reconnect_delay); + warning_said = true; + } + fiber_close(); + fiber_sleep(reconnect_delay); + } +} + +static void +pull_from_remote(void *state) +{ + struct remote_state *h = state; + struct tbuf *row; + + if (setjmp(fiber->exc) != 0) + fiber_close(); - next_lsn(r, r->wal_class.row_lsn(row)); - confirm_lsn(r, r->wal_class.row_lsn(row)); + for (;;) { + row = remote_read_row(h->r->confirmed_lsn + 1); + h->r->recovery_lag = ev_now() - row_v11(row)->tm; + h->r->recovery_last_update_tstamp = ev_now(); - if (rows++ % 1000 == 0) { - prelease(fiber->pool); - rows = 0; + if (h->handler(h->r, row) < 0) { + fiber_close(); + continue; } + + fiber_gc(); } } +int +default_remote_row_handler(struct recovery_state *r, struct tbuf *row) +{ + struct tbuf *data; + i64 lsn = row_v11(row)->lsn; + u16 tag; + + /* save row data since wal_row_handler may clobber it */ + data = tbuf_alloc(row->pool); + tbuf_append(data, row_v11(row)->data, row_v11(row)->len); + + if (r->row_handler(r, row) < 0) + panic("replication failure: can't apply row"); + + tag = read_u16(data); + (void)read_u64(data); /* drop the cookie */ + + if (wal_write(r, tag, r->cookie, lsn, data) == false) + panic("replication failure: can't write row to WAL"); + + next_lsn(r, lsn); + confirm_lsn(r, lsn); + + return 0; +} struct fiber * -recover_follow_remote(struct recovery_state *r, char *ip_addr, int port) +recover_follow_remote(struct recovery_state *r, char *ip_addr, int port, + int (*handler) (struct recovery_state *r, struct tbuf *row)) { char *name; struct fiber *f; struct in_addr server; struct sockaddr_in *addr; + struct remote_state *h; say_crit("initializing remote hot standby, WAL feeder %s:%i", ip_addr, port); name = palloc(eter_pool, 64); snprintf(name, 64, "remote_hot_standby/%s:%i", ip_addr, port); - f = fiber_create(name, -1, -1, pull_from_remote, r); + + h = palloc(eter_pool, sizeof(*h)); + h->r = r; + h->handler = handler; + + f = fiber_create(name, -1, -1, pull_from_remote, h); if (f == NULL) return NULL; - if (inet_aton(ip_addr, &server) < 0) { say_syserror("inet_aton: %s", ip_addr); return NULL; @@ -144,6 +213,7 @@ recover_follow_remote(struct recovery_state *r, char *ip_addr, int port) memcpy(&addr->sin_addr.s_addr, &server, sizeof(server)); addr->sin_port = htons(port); f->data = addr; + memcpy(&r->cookie, &addr, MIN(sizeof(r->cookie), sizeof(addr))); fiber_call(f); return f; } diff --git a/core/palloc.c b/core/palloc.c index 728914ce7734337198b7b490c5de100df733a9ba..ee82319c10c8ca92ee0619d57fc143abf2c12705 100644 --- a/core/palloc.c +++ b/core/palloc.c @@ -40,21 +40,21 @@ struct chunk { uint32_t magic; + void *brk; + size_t free; size_t size; - size_t allocated; - struct chunk_class * restrict class; + struct chunk_class *class; SLIST_ENTRY(chunk) busy_link; SLIST_ENTRY(chunk) free_link; - - unsigned char data[0]; }; SLIST_HEAD(chunk_list_head, chunk); struct chunk_class { int i; - i64 size; + u32 size; + int chunks_count; struct chunk_list_head chunks; TAILQ_ENTRY(chunk_class) link; }; @@ -86,12 +86,31 @@ struct palloc_pool *eter_pool; #define PALLOC_POISON #endif -size_t +static size_t palloc_greatest_size(void) { return (1 << 22) - sizeof(struct chunk); } + +static struct chunk_class * +class_init(size_t size) +{ + struct chunk_class *class; + + class = malloc(sizeof(struct chunk_class)); + if (class == NULL) + return NULL; + + class->i = class_count++; + class->chunks_count = 0; + class->size = size; + SLIST_INIT(&class->chunks); + TAILQ_INSERT_TAIL(&classes, class, link); + + return class; +} + int palloc_init(void) { @@ -100,24 +119,17 @@ palloc_init(void) class_count = 0; TAILQ_INIT(&classes); - for (size_t size = 4096; size <= 1 << 16; size *= 2) { - class = malloc(sizeof(struct chunk_class)); - class->i = class_count++; - if (class == NULL) + for (size_t size = 4096 * 8; size <= 1 << 16; size *= 2) { + if (class_init(size) == NULL) return 0; - - class->size = size - sizeof(struct chunk); - SLIST_INIT(&class->chunks); - TAILQ_INSERT_TAIL(&classes, class, link); } - class = malloc(sizeof(struct chunk_class)); - class->i = class_count++; - if (class == NULL) + if (class_init(palloc_greatest_size()) == NULL) return 0; - class->size = palloc_greatest_size(); - SLIST_INIT(&class->chunks); - TAILQ_INSERT_TAIL(&classes, class, link); + + if ((class = class_init(-1)) == NULL) + return 0; + TAILQ_NEXT(class, link) = NULL; eter_pool = palloc_create_pool("eter_pool"); @@ -129,27 +141,30 @@ poison_chunk(struct chunk *chunk) { (void)chunk; /* arg used */ #ifdef PALLOC_POISON - memset(chunk->data, poison_char, chunk->size); + memset((void *)chunk + sizeof(struct chunk), poison_char, chunk->size); VALGRIND_MAKE_MEM_NOACCESS(chunk->data, chunk->size); #endif } static struct chunk * -next_chunk_for(struct palloc_pool * restrict pool, size_t size) +next_chunk_for(struct palloc_pool *restrict pool, size_t size) { - struct chunk * restrict chunk = SLIST_FIRST(&pool->chunks); - struct chunk_class * restrict class; + struct chunk *restrict chunk = SLIST_FIRST(&pool->chunks); + struct chunk_class *restrict class; + size_t chunk_size; if (chunk != NULL) class = chunk->class; else class = TAILQ_FIRST(&classes); + if (class->size == -1) + class = TAILQ_PREV(class, class_tailq_head, link); + while (class != NULL && class->size < size) class = TAILQ_NEXT(class, link); - if (class == NULL) - return NULL; + assert(class != NULL); chunk = SLIST_FIRST(&class->chunks); if (chunk != NULL) { @@ -157,14 +172,21 @@ next_chunk_for(struct palloc_pool * restrict pool, size_t size) goto found; } - chunk = malloc(class->size + sizeof(struct chunk)); + if (size > palloc_greatest_size()) + chunk_size = size; + else + chunk_size = class->size; + + chunk = malloc(chunk_size + sizeof(struct chunk)); if (chunk == NULL) return NULL; + class->chunks_count++; chunk->magic = chunk_magic; - chunk->allocated = 0; - chunk->size = class->size; + chunk->size = chunk_size; + chunk->free = chunk_size; + chunk->brk = (void *)chunk + sizeof(struct chunk); chunk->class = class; found: assert(chunk != NULL && chunk->magic == chunk_magic); @@ -174,24 +196,6 @@ next_chunk_for(struct palloc_pool * restrict pool, size_t size) return chunk; } - -static void * -alloc_from_chunk(struct chunk *chunk, size_t size) -{ - assert(chunk != NULL); - assert(chunk->magic == chunk_magic); - assert(chunk->size >= chunk->allocated); - - if (likely(chunk->size - chunk->allocated >= size)) { - void *ptr = chunk->data + chunk->allocated; // TODO: align ptr - chunk->allocated += size; - return ptr; - } else { - return NULL; - } -} - - #ifndef NDEBUG static const char * poisoned(const char *b, size_t size) @@ -207,68 +211,77 @@ poisoned(const char *b, size_t size) } #endif - -static void * __noinline__ -palloc_slow_path(struct palloc_pool * restrict pool, size_t size) +static void *__noinline__ +palloc_slow_path(struct palloc_pool *restrict pool, size_t size) { - struct chunk * restrict chunk; + struct chunk *chunk; chunk = next_chunk_for(pool, size); - assert(chunk != NULL); - return alloc_from_chunk(chunk, size); + if (chunk == NULL) + abort(); + + assert(chunk->free >= size); + void *ptr = chunk->brk; + chunk->brk += size; + chunk->free -= size; + return ptr; } -void * -palloc(struct palloc_pool *pool, size_t size) +void *__regparm2__ +palloc(struct palloc_pool *restrict pool, size_t size) { - assert(size < palloc_greatest_size()); - size_t rz_size = size + PALLOC_REDZONE * 2; - struct chunk * restrict chunk = SLIST_FIRST(&pool->chunks); + const size_t rz_size = size + PALLOC_REDZONE * 2; + struct chunk *restrict chunk = SLIST_FIRST(&pool->chunks); void *ptr; pool->allocated += rz_size; - ptr = alloc_from_chunk(chunk, rz_size); - if (unlikely(ptr == NULL)) + + if (likely(chunk->free >= rz_size)) { + ptr = chunk->brk; + chunk->brk += rz_size; + chunk->free -= rz_size; + } else ptr = palloc_slow_path(pool, rz_size); - assert(ptr != NULL); assert(poisoned(ptr + PALLOC_REDZONE, size) == NULL); VALGRIND_MEMPOOL_ALLOC(pool, ptr + PALLOC_REDZONE, size); + return ptr + PALLOC_REDZONE; } -void * +void *__regparm2__ p0alloc(struct palloc_pool *pool, size_t size) { - void * ptr; + void *ptr; ptr = palloc(pool, size); - if (ptr) - memset(ptr, 0, size); - + memset(ptr, 0, size); return ptr; } void * palloca(struct palloc_pool *pool, size_t size, size_t align) { - void * ptr; + void *ptr; ptr = palloc(pool, size + align); - return (void*)TYPEALIGN(align, (uintptr_t)ptr); + return (void *)TYPEALIGN(align, (uintptr_t)ptr); } void prelease(struct palloc_pool *pool) { - struct chunk *chunk; - - for (chunk = SLIST_FIRST(&pool->chunks); - chunk != NULL; - chunk = SLIST_NEXT(chunk, busy_link)) - { - chunk->allocated = 0; - SLIST_INSERT_HEAD(&chunk->class->chunks, chunk, free_link); - poison_chunk(chunk); + struct chunk *chunk, *next_chunk; + + for (chunk = SLIST_FIRST(&pool->chunks); chunk != NULL; chunk = next_chunk) { + next_chunk = SLIST_NEXT(chunk, busy_link); + if (chunk->size <= palloc_greatest_size()) { + chunk->free = chunk->size; + chunk->brk = (void *)chunk + sizeof(struct chunk); + SLIST_INSERT_HEAD(&chunk->class->chunks, chunk, free_link); + poison_chunk(chunk); + } else { + free(chunk); + } } SLIST_INIT(&pool->chunks); @@ -277,6 +290,13 @@ prelease(struct palloc_pool *pool) next_chunk_for(pool, 128); } +void +prelease_after(struct palloc_pool *pool, size_t after) +{ + if (pool->allocated > after) + prelease(pool); +} + struct palloc_pool * palloc_create_pool2(const char *name, size_t initial_size) { @@ -319,14 +339,14 @@ palloc_stat(struct tbuf *buf) tbuf_printf(buf, "palloc statistic:\n"); tbuf_printf(buf, " classes:\n"); TAILQ_FOREACH(class, &classes, link) { - int free_chunks = 0, busy_chunks = 0; + int free_chunks = 0; SLIST_FOREACH(chunk, &class->chunks, free_link) - free_chunks++; - SLIST_FOREACH(chunk, &class->chunks, busy_link) - busy_chunks++; + free_chunks++; - tbuf_printf(buf, " - { tsize: %- 8"PRIi64", free_chunks: %- 6i, busy_chunks: %- 6i }\n", - class->size, free_chunks, busy_chunks); + tbuf_printf(buf, + " - { size: %"PRIu32 + ", free_chunks: %- 6i, busy_chunks: %- 6i }\n", class->size, + free_chunks, class->chunks_count - free_chunks); } tbuf_printf(buf, " pools:\n"); @@ -347,7 +367,7 @@ palloc_stat(struct tbuf *buf) TAILQ_FOREACH(class, &classes, link) { if (chunks[class->i] == 0) continue; - tbuf_printf(buf, " - { size: %- 7"PRIi64", used: %i }\n", + tbuf_printf(buf, " - { size: %"PRIu32", used: %i }\n", class->size, chunks[class->i]); if (indent == 0) @@ -365,3 +385,9 @@ palloc_name(struct palloc_pool *pool, const char *new_name) pool->name = new_name; return old_name; } + +size_t +palloc_allocated(struct palloc_pool *pool) +{ + return pool->allocated; +} diff --git a/core/pickle.c b/core/pickle.c index a2971bfe2d837269e82b3083c4e68bfbdf2cca3c..5f385cf50455aa78693378e46d4fec9cde4e1964 100644 --- a/core/pickle.c +++ b/core/pickle.c @@ -30,7 +30,7 @@ #include <tbuf.h> #include <palloc.h> #include <fiber.h> -#include <iproto.h> /* for err codes */ +#include <iproto.h> /* for err codes */ #include <pickle.h> /* caller must ensure that there is space in target */ @@ -54,7 +54,6 @@ save_varint32(u8 *target, u32 value) return target; } - inline static void append_byte(struct tbuf *b, u8 byte) { @@ -80,34 +79,22 @@ write_varint32(struct tbuf *b, u32 value) append_byte(b, (u8)((value) & 0x7F)); } +#define read_u(bits) \ + u##bits read_u##bits(struct tbuf *b) \ + { \ + if (b->len < (bits)/8) \ + raise(ERR_CODE_UNKNOWN_ERROR, "buffer too short"); \ + u##bits r = *(u##bits *)b->data; \ + b->size -= (bits)/8; \ + b->len -= (bits)/8; \ + b->data += (bits)/8; \ + return r; \ + } -u32 -read_u32(struct tbuf *b) -{ - if (b->len < 4) - raise(ERR_CODE_UNKNOWN_ERROR, "buffer too short"); - - u32 r = *(u32 *)b->data; /* FIXME: endianess & aligment */ - b->size -= 4; - b->len -= 4; - b->data += 4; - - return r; -} - -u8 -read_u8(struct tbuf *b) -{ - if (b->len < 1) - raise(ERR_CODE_UNKNOWN_ERROR, "buffer too short"); - - u8 r = *(u8 *)b->data; - b->size -= 1; - b->len -= 1; - b->data += 1; - - return r; -} +read_u(8) +read_u(16) +read_u(32) +read_u(64) u32 read_varint32(struct tbuf *buf) @@ -117,7 +104,7 @@ read_varint32(struct tbuf *buf) if (len < 1) raise(ERR_CODE_UNKNOWN_ERROR, "buffer too short"); - if(!(b[0] & 0x80)) { + if (!(b[0] & 0x80)) { buf->data += 1; buf->size -= 1; buf->len -= 1; @@ -126,7 +113,7 @@ read_varint32(struct tbuf *buf) if (len < 2) raise(ERR_CODE_UNKNOWN_ERROR, "buffer too short"); - if(!(b[1] & 0x80)) { + if (!(b[1] & 0x80)) { buf->data += 2; buf->size -= 2; buf->len -= 2; @@ -134,7 +121,7 @@ read_varint32(struct tbuf *buf) } if (len < 3) raise(ERR_CODE_UNKNOWN_ERROR, "buffer too short"); - if(!(b[2] & 0x80)) { + if (!(b[2] & 0x80)) { buf->data += 3; buf->size -= 3; buf->len -= 3; @@ -143,7 +130,7 @@ read_varint32(struct tbuf *buf) if (len < 4) raise(ERR_CODE_UNKNOWN_ERROR, "buffer too short"); - if(!(b[3] & 0x80)) { + if (!(b[3] & 0x80)) { buf->data += 4; buf->size -= 4; buf->len -= 4; @@ -153,7 +140,7 @@ read_varint32(struct tbuf *buf) if (len < 5) raise(ERR_CODE_UNKNOWN_ERROR, "buffer too short"); - if(!(b[4] & 0x80)) { + if (!(b[4] & 0x80)) { buf->data += 5; buf->size -= 5; buf->len -= 5; @@ -165,7 +152,6 @@ read_varint32(struct tbuf *buf) return 0; } - u32 pick_u32(void *data, void **rest) { @@ -190,7 +176,6 @@ read_field(struct tbuf *buf) return p; } - u32 valid_tuple(struct tbuf *buf, u32 cardinality) { diff --git a/core/salloc.c b/core/salloc.c index 398a1cff1179d374df836cf7e1425d0dc3829e13..7b845abcd752c10b02dac41a87421ad353aa4912 100644 --- a/core/salloc.c +++ b/core/salloc.c @@ -42,9 +42,9 @@ #ifdef SLAB_DEBUG #undef NDEBUG -u8 red_zone[4] = {0xfa, 0xfa, 0xfa, 0xfa}; +u8 red_zone[4] = { 0xfa, 0xfa, 0xfa, 0xfa }; #else -u8 red_zone[0] = {}; +u8 red_zone[0] = { }; #endif const u32 SLAB_MAGIC = 0x51abface; @@ -61,10 +61,11 @@ struct slab { size_t items; struct slab_item *free; struct slab_class *class; - struct arena *arena; - TAILQ_ENTRY(slab) link; - TAILQ_ENTRY(slab) free_link; - SLIST_ENTRY(slab) class_link; + void *brk; + SLIST_ENTRY(slab) link; + SLIST_ENTRY(slab) free_link; + TAILQ_ENTRY(slab) class_free_link; + TAILQ_ENTRY(slab) class_link; }; SLIST_HEAD(slab_slist_head, slab); @@ -72,8 +73,7 @@ TAILQ_HEAD(slab_tailq_head, slab); struct slab_class { size_t item_size; - struct slab_tailq_head free_slabs; - struct slab_slist_head slabs; + struct slab_tailq_head slabs, free_slabs; }; struct arena { @@ -89,7 +89,7 @@ size_t slab_active_classes; struct slab_class slab_classes[256]; struct arena arena; -struct slab_tailq_head slabs; +struct slab_slist_head slabs, free_slabs; static struct slab * slab_header(void *ptr) @@ -109,11 +109,12 @@ slab_classes_init(size_t minimal, double factor) slab_classes[i].item_size = size - sizeof(red_zone); TAILQ_INIT(&slab_classes[i].free_slabs); - size = MAX((size_t) (size * factor) & ~(ptr_size - 1), + size = MAX((size_t)(size * factor) & ~(ptr_size - 1), (size + ptr_size) & ~(ptr_size - 1)); } - TAILQ_INIT(&slabs); + SLIST_INIT(&slabs); + SLIST_INIT(&free_slabs); slab_active_classes = i; } @@ -164,7 +165,7 @@ salloc_init(size_t size, size_t minimal, double factor) if (!arena_init(&arena, size)) return false; - slab_classes_init(MAX(sizeof(void *),minimal), factor); + slab_classes_init(MAX(sizeof(void *), minimal), factor); return true; } @@ -178,35 +179,25 @@ salloc_destroy(void) } static void -format_slab(struct arena *arena, struct slab_class *class, struct slab *slab) +format_slab(struct slab_class *class, struct slab *slab) { - char *p; - struct slab_item *prev, *curr = NULL; - assert(class->item_size <= MAX_SLAB_ITEM); slab->magic = SLAB_MAGIC; - slab->free = (void *)((char *)slab + sizeof(struct slab)); + slab->free = NULL; slab->class = class; - slab->arena = arena; - - curr = NULL; - prev = slab->free; - for (p = (char *)slab + sizeof(struct slab) + class->item_size + sizeof(red_zone); - p + class->item_size < (char *)slab + SLAB_SIZE; - p += class->item_size + sizeof(red_zone)) - { - curr = (struct slab_item *)p; - prev->next = curr; - memcpy((char *)prev + class->item_size, red_zone, sizeof(red_zone)); - prev = curr; - } - curr->next = NULL; - memcpy((char *)curr + class->item_size, red_zone, sizeof(red_zone)); + slab->items = 0; + slab->used = 0; + slab->brk = (void *)CACHEALIGN((void *)slab + sizeof(struct slab)); - SLIST_INSERT_HEAD(&class->slabs, slab, class_link); - TAILQ_INSERT_HEAD(&class->free_slabs, slab, free_link); - TAILQ_INSERT_HEAD(&slabs, slab, link); + TAILQ_INSERT_HEAD(&class->slabs, slab, class_link); + TAILQ_INSERT_HEAD(&class->free_slabs, slab, class_free_link); +} + +static bool +full_formated(struct slab *slab) +{ + return slab->brk + slab->class->item_size >= (void *)slab + SLAB_SIZE; } void @@ -214,7 +205,7 @@ slab_validate(void) { struct slab *slab; - TAILQ_FOREACH(slab, &slabs, link) { + SLIST_FOREACH(slab, &slabs, link) { for (char *p = (char *)slab + sizeof(struct slab); p + slab->class->item_size < (char *)slab + SLAB_SIZE; p += slab->class->item_size + sizeof(red_zone)) { @@ -236,24 +227,37 @@ class_for(size_t size) static struct slab * slab_of(struct slab_class *class) { - struct slab *slab = TAILQ_FIRST(&class->free_slabs); + struct slab *slab; - if (slab != NULL) { + if (!TAILQ_EMPTY(&class->free_slabs)) { + slab = TAILQ_FIRST(&class->free_slabs); assert(slab->magic == SLAB_MAGIC); return slab; } - if ((slab = arena_alloc(&arena)) == NULL) - return NULL; - format_slab(&arena, class, slab); - return slab; + + if (!SLIST_EMPTY(&free_slabs)) { + slab = SLIST_FIRST(&free_slabs); + assert(slab->magic == SLAB_MAGIC); + SLIST_REMOVE_HEAD(&free_slabs, free_link); + format_slab(class, slab); + return slab; + } + + if ((slab = arena_alloc(&arena)) != NULL) { + format_slab(class, slab); + SLIST_INSERT_HEAD(&slabs, slab, link); + return slab; + } + + return NULL; } #ifndef NDEBUG static bool valid_item(struct slab *slab, void *item) { - return (u8*)item >= (u8 *)(slab) + sizeof(struct slab) && - (u8*)item < (u8 *)(slab) + sizeof(struct slab) + SLAB_SIZE; + return (void *)item >= (void *)(slab) + sizeof(struct slab) && + (void *)item < (void *)(slab) + sizeof(struct slab) + SLAB_SIZE; } #endif @@ -270,18 +274,26 @@ salloc(size_t size) if ((slab = slab_of(class)) == NULL) return NULL; - assert(valid_item(slab, slab->free)); + if (slab->free == NULL) { + assert(valid_item(slab, slab->brk)); + item = slab->brk; + memcpy((void *)item + class->item_size, red_zone, sizeof(red_zone)); + slab->brk += class->item_size + sizeof(red_zone); + } else { + assert(valid_item(slab, slab->free)); + item = slab->free; + + VALGRIND_MAKE_MEM_DEFINED(item, sizeof(void *)); + slab->free = item->next; + VALGRIND_MAKE_MEM_UNDEFINED(item, sizeof(void *)); + } + + if (full_formated(slab) && slab->free == NULL) + TAILQ_REMOVE(&class->free_slabs, slab, class_free_link); - item = slab->free; - VALGRIND_MAKE_MEM_DEFINED(item, sizeof(void *)); - slab->free = item->next; - VALGRIND_MAKE_MEM_UNDEFINED(item, sizeof(void *)); slab->used += class->item_size + sizeof(red_zone); slab->items += 1; - if (slab->free == NULL) - TAILQ_REMOVE(&class->free_slabs, slab, free_link); - VALGRIND_MALLOCLIKE_BLOCK(item, class->item_size, sizeof(red_zone), 0); return (void *)item; } @@ -294,8 +306,8 @@ sfree(void *ptr) struct slab_class *class = slab->class; struct slab_item *item = ptr; - if (slab->free == NULL) - TAILQ_INSERT_TAIL(&class->free_slabs, slab, free_link); + if (full_formated(slab) && slab->free == NULL) + TAILQ_INSERT_TAIL(&class->free_slabs, slab, class_free_link); assert(valid_item(slab, item)); assert(slab->free == NULL || valid_item(slab, slab->free)); @@ -304,6 +316,13 @@ sfree(void *ptr) slab->free = item; slab->used -= class->item_size + sizeof(red_zone); slab->items -= 1; + + if (slab->items == 0) { + TAILQ_REMOVE(&class->free_slabs, slab, class_free_link); + TAILQ_REMOVE(&class->slabs, slab, class_link); + SLIST_INSERT_HEAD(&free_slabs, slab, free_link); + } + VALGRIND_FREELIKE_BLOCK(item, sizeof(red_zone)); } @@ -312,23 +331,29 @@ slab_stat(struct tbuf *t) { struct slab *slab; int slabs; - i64 items, used, free; + i64 items, used, free, total_used = 0; tbuf_printf(t, "slab statistics:\n classes:\n"); for (int i = 0; i < slab_active_classes; i++) { slabs = items = used = free = 0; - SLIST_FOREACH(slab, &slab_classes[i].slabs, class_link) { - if (slab->free != NULL) - free += SLAB_SIZE - slab->used - sizeof(struct slab); + TAILQ_FOREACH(slab, &slab_classes[i].slabs, class_link) { + free += SLAB_SIZE - slab->used - sizeof(struct slab); items += slab->items; - used += slab->used; + used += sizeof(struct slab) + slab->used; + total_used += sizeof(struct slab) + slab->used; slabs++; } - if (used == 0) + + if (slabs == 0) continue; - tbuf_printf(t, " - { item_size: %- 5i, slabs: %- 3i, items: %- 11"PRIi64", bytes_used: %- 12"PRIi64", bytes_free: %- 12"PRIi64" }\n", + tbuf_printf(t, + " - { item_size: %- 5i, slabs: %- 3i, items: %- 11" PRIi64 + ", bytes_used: %- 12" PRIi64 ", bytes_free: %- 12" PRIi64 " }\n", (int)slab_classes[i].item_size, slabs, items, used, free); + } + tbuf_printf(t, " items_used: %.2f\n", (double)total_used / arena.size * 100); + tbuf_printf(t, " arena_used: %.2f\n", (double)arena.used / arena.size * 100); } void @@ -337,7 +362,7 @@ slab_stat2(u64 *bytes_used, u64 *items) struct slab *slab; *bytes_used = *items = 0; - TAILQ_FOREACH(slab, &slabs, link) { + SLIST_FOREACH(slab, &slabs, link) { *bytes_used += slab->used; *items += slab->items; } diff --git a/core/say.c b/core/say.c index c736aca64a59378904bcbf0e2fba74f715fb78b6..73d1266a5f3597eea1464ba55cdf1cd47663e47e 100644 --- a/core/say.c +++ b/core/say.c @@ -65,8 +65,8 @@ say_logger_init(int nonblock) { int pipefd[2]; pid_t pid; - char *argv[] = {"/bin/sh", "-c", cfg.logger, NULL}; - char *envp[] = {NULL}; + char *argv[] = { "/bin/sh", "-c", cfg.logger, NULL }; + char *envp[] = { NULL }; if (cfg.logger != NULL) { if (pipe(pipefd) == -1) { @@ -93,7 +93,7 @@ say_logger_init(int nonblock) } else { sayfd = STDERR_FILENO; } -out: + out: if (nonblock) set_nonblock(sayfd); } @@ -105,6 +105,8 @@ vsay(int level, const char *error, const char *format, va_list ap) size_t p = 0, len = PIPE_BUF; static char buf[PIPE_BUF]; + ev_now_update(); + if (peer_name == NULL) peer_name = "_"; diff --git a/core/stat.c b/core/stat.c index 1dbe7642832547c59f414d898d77213963fed2d2..c515cd0d1be53bf3ece5061f33a6c4a82ff3f602 100644 --- a/core/stat.c +++ b/core/stat.c @@ -28,38 +28,53 @@ #include <tarantool_ev.h> #include <tbuf.h> #include <say.h> +#include <stat.h> #include <third_party/khash.h> -KHASH_MAP_INIT_STR(char2int, i64, realloc); - #define SECS 5 -static -khash_t(char2int) * -stats[SECS], *stats_all; static ev_timer timer; -void -stat_collect(const char *name, int value) +struct { + char *name; + i64 value[SECS + 1]; +} *stats = NULL; +static int stats_size = 0; +static int stats_max = 0; +static int base = 0; + +int +stat_register(char **name, size_t count) { - int ret; - khiter_t k; - - k = kh_put(char2int, stats[0], name, &ret); - if (ret == 0) { - kh_value(stats[0], k) += value; - } else { - kh_key(stats[0], k) = name; - kh_value(stats[0], k) = value; - } + int initial_base = base; + + for (int i = 0; i < count; i++, name++, base++) { + if (stats_size <= base) { + stats_size += 1024; + stats = realloc(stats, sizeof(*stats) * stats_size); + if (stats == NULL) + abort(); + } - k = kh_put(char2int, stats_all, name, &ret); - if (ret == 0) { - kh_value(stats_all, k) += value; - } else { - kh_key(stats_all, k) = name; - kh_value(stats_all, k) = value; + stats[base].name = *name; + + if (*name == NULL) + continue; + + for (int i = 0; i < SECS + 1; i++) + stats[base].value[i] = 0; + + stats_max = base; } + + return initial_base; +} + +void +stat_collect(int base, int name, i64 value) +{ + stats[base + name].value[0] += value; + stats[base + name].value[SECS] += value; } void @@ -68,50 +83,46 @@ stat_print(struct tbuf *buf) int max_len = 0; tbuf_printf(buf, "statistics:\n"); - for (khiter_t k = kh_begin(stats_all); k != kh_end(stats_all); ++k) { - if (!kh_exist(stats_all, k)) + for (int i = 0; i < stats_max; i++) { + if (stats[i].name == NULL) continue; - - const char *key = kh_key(stats_all, k); - max_len = MAX(max_len, strlen(key)); + max_len = MAX(max_len, strlen(stats[i].name)); } - for (khiter_t k = kh_begin(stats_all); k != kh_end(stats_all); ++k) { - if (!kh_exist(stats_all, k)) + for (int i = 0; i < stats_max; i++) { + if (stats[i].name == NULL) continue; - const char *key = kh_key(stats_all, k); - int value = 0; - for (int i = 0; i < SECS; i++) { - khiter_t j = kh_get(char2int, stats[i], key); - if (j != kh_end(stats[i])) - value += kh_value(stats[i], j); - } - tbuf_printf(buf, " %s:%*s{ rps:%- 6i, total:%- 12" PRIi64 " }\n", - key, 1 + max_len - (int)strlen(key), " ", - value / SECS, kh_value(stats_all, k)); + int diff = 0; + for (int j = 0; j < SECS; j++) + diff += stats[i].value[j]; + + diff /= SECS; + + tbuf_printf(buf, " %s:%*s{ rps: %- 6i, total: %- 12" PRIi64 " }\n", + stats[i].name, 1 + max_len - (int)strlen(stats[i].name), " ", + diff, stats[i].value[SECS]); } } void -stat_age(ev_timer * timer, int events __unused__) +stat_age(ev_timer *timer, int events __unused__) { - khash_t(char2int) * last = stats[SECS - 1]; - for (int i = 0; i < SECS - 1; i++) { - stats[i + 1] = stats[i]; + for (int i = 0; i < stats_max; i++) { + if (stats[i].name == NULL) + continue; + + for (int j = SECS - 2; j >= 0; j--) + stats[i].value[j + 1] = stats[i].value[j]; + stats[i].value[0] = 0; } - stats[0] = last; - kh_clear(char2int, last); + ev_timer_again(timer); } void stat_init(void) { - stats_all = kh_init(char2int, NULL); - for (int i = 0; i < SECS; i++) - stats[i] = kh_init(char2int, NULL); - ev_init(&timer, stat_age); timer.repeat = 1.; ev_timer_again(&timer); diff --git a/core/tarantool.c b/core/tarantool.c index 600d868772e5f31b75f7d0ce80c8ee486dd5ad00..8e89ba11990b9908e487a587d5019fdd419c8a96 100644 --- a/core/tarantool.c +++ b/core/tarantool.c @@ -62,8 +62,8 @@ enum tarantool_role role = def; extern int daemonize(int nochdir, int noclose); - -const char *tarantool_version(void) +const char * +tarantool_version(void) { return tarantool_version_string; } @@ -88,7 +88,7 @@ snapshot(void *ev __unused__, int events __unused__) return; fiber->name = "dumper"; - set_proc_title("dumper (%"PRIu32")", getppid()); + set_proc_title("dumper (%" PRIu32 ")", getppid()); close_all_xcpt(1, sayfd); snapshot_save(recovery_state, mod_snapshot); #ifdef COVERAGE @@ -103,7 +103,7 @@ sig_child(int signal __unused__) { int child_status; /* TODO: watch child status & destroy corresponding fibers */ - while (waitpid(-1, &child_status, WNOHANG) > 0); + while (waitpid(-1, &child_status, WNOHANG) > 0) ; } static void @@ -112,13 +112,12 @@ sig_int(int signal) say_info("SIGINT or SIGTERM recieved, terminating"); if (recovery_state != NULL) { - struct child *writer = wal_writer(recovery_state); + struct child *writer = recovery_state->wal_writer; if (writer && writer->out && writer->out->fd > 0) { close(writer->out->fd); usleep(1000); } } - #ifdef COVERAGE __gcov_flush(); #endif @@ -166,17 +165,16 @@ signal_init(void) goto error; return; -error: + error: say_syserror("sigaction"); exit(EX_OSERR); } - static void create_pid(void) { FILE *f; - char buf[16] = {0}; + char buf[16] = { 0 }; pid_t pid; if (cfg.pid_file == NULL) @@ -207,8 +205,6 @@ remove_pid(void) unlink(cfg.pid_file); } - - static void initialize(double slab_alloc_arena, int slab_alloc_minimal, double slab_alloc_factor) { @@ -217,7 +213,6 @@ initialize(double slab_alloc_arena, int slab_alloc_minimal, double slab_alloc_fa panic("can't initialize slab allocator"); fiber_init(); - signal_init(); } static void @@ -244,44 +239,44 @@ main(int argc, char **argv) const char *short_opt = "c:pvVD"; const struct option long_opt[] = { - { .name = "config", - .has_arg = 1, - .flag = NULL, - .val = 'c' }, + {.name = "config", + .has_arg = 1, + .flag = NULL, + .val = 'c'}, #ifdef STORAGE - { .name = "cat", - .has_arg = 1, - .flag = NULL, - .val = 'C' }, - { .name = "init_storage", - .has_arg = 0, - .flag = NULL, - .val = 'I'}, + {.name = "cat", + .has_arg = 1, + .flag = NULL, + .val = 'C'}, + {.name = "init_storage", + .has_arg = 0, + .flag = NULL, + .val = 'I'}, #endif - { .name = "create_pid", - .has_arg = 0, - .flag = NULL, - .val = 'p'}, - { .name = "verbose", - .has_arg = 0, - .flag = NULL, - .val = 'v'}, - { .name = "version", - .has_arg = 0, - .flag = NULL, - .val = 'V'}, - { .name = "daemonize", - .has_arg = 0, - .flag = NULL, - .val = 'D'}, - { .name = NULL, - .has_arg = 0, - .flag = NULL, - .val = 0 } + {.name = "create_pid", + .has_arg = 0, + .flag = NULL, + .val = 'p'}, + {.name = "verbose", + .has_arg = 0, + .flag = NULL, + .val = 'v'}, + {.name = "version", + .has_arg = 0, + .flag = NULL, + .val = 'V'}, + {.name = "daemonize", + .has_arg = 0, + .flag = NULL, + .val = 'D'}, + {.name = NULL, + .has_arg = 0, + .flag = NULL, + .val = 0} }; while ((c = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) { - switch (c) { + switch (c) { case 'V': puts(tarantool_version()); return 0; @@ -290,7 +285,7 @@ main(int argc, char **argv) panic("no arg given"); cfg_filename = strdup(optarg); break; - case 'C': + case 'C': if (optarg == NULL) panic("no arg given"); cat_filename = strdup(optarg); @@ -346,12 +341,12 @@ main(int argc, char **argv) fill_default_tarantool_cfg(&cfg); parse_cfg_file_tarantool_cfg(&cfg, f, 0, &n_accepted, &n_skipped); + check_cfg_tarantool_cfg(&cfg); fclose(f); #ifdef STORAGE if (role == cat) { initialize_minimal(); - mod_init(); if (access(cat_filename, R_OK) == -1) { say_syserror("access(\"%s\")", cat_filename); exit(EX_OSFILE); @@ -384,7 +379,7 @@ main(int argc, char **argv) } if (cfg.coredump) { - struct rlimit c = {0,0}; + struct rlimit c = { 0, 0 }; if (getrlimit(RLIMIT_CORE, &c) < 0) { say_syserror("getrlimit"); exit(EX_OSERR); @@ -394,15 +389,13 @@ main(int argc, char **argv) say_syserror("setrlimit"); exit(EX_OSERR); } - #ifdef Linux - if (prctl(PR_SET_DUMPABLE,1,0,0,0) < 0) { + if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) { say_syserror("prctl"); exit(EX_OSERR); } #endif } - #ifdef STORAGE if (init_storage) { initialize_minimal(); @@ -425,6 +418,7 @@ main(int argc, char **argv) #if defined(UTILITY) initialize_minimal(); + signal_init(); mod_init(); #elif defined(STORAGE) ev_signal *ev_sig; @@ -433,10 +427,12 @@ main(int argc, char **argv) ev_signal_start(ev_sig); initialize(cfg.slab_alloc_arena, cfg.slab_alloc_minimal, cfg.slab_alloc_factor); + signal_init(); ev_default_loop(0); mod_init(); admin_init(); + prelease(fiber->pool); say_crit("log level %i", cfg.log_level); say_crit("entering event loop"); if (cfg.io_collect_interval > 0) diff --git a/core/tbuf.c b/core/tbuf.c index efca9e588a1efd9dca3abc01a3f971685fa7998a..37eb6ce5fd496f5969d24c16311e5a73c7e1e086 100644 --- a/core/tbuf.c +++ b/core/tbuf.c @@ -44,13 +44,11 @@ # define poison(ptr, len) #endif - static void -tbuf_assert(const struct tbuf * b) +tbuf_assert(const struct tbuf *b) { - (void)b; /* arg used :-) */ + (void)b; /* arg used :-) */ assert(b->len <= b->size); - assert(b->size <= palloc_greatest_size()); } struct tbuf * @@ -58,10 +56,10 @@ tbuf_alloc(struct palloc_pool *pool) { const size_t initial_size = 128 - sizeof(struct tbuf); struct tbuf *e = palloc(pool, sizeof(*e) + initial_size); - e->pool = pool; - e->data = (char *)e + sizeof(*e); - e->size = initial_size; e->len = 0; + e->size = initial_size; + e->data = (char *)e + sizeof(*e); + e->pool = pool; poison(e->data, e->size); tbuf_assert(e); return e; @@ -78,11 +76,6 @@ tbuf_ensure_resize(struct tbuf *e, size_t required) while (new_size - e->len < required) new_size *= 2; - if (unlikely(new_size > palloc_greatest_size())) { - new_size = palloc_greatest_size(); - assert(new_size - e->len >= required); - } - void *p = palloc(e->pool, new_size); poison(p, new_size); @@ -132,7 +125,7 @@ tbuf_peek(struct tbuf *b, size_t count) } size_t -tbuf_reserve(struct tbuf * b, size_t count) +tbuf_reserve(struct tbuf *b, size_t count) { tbuf_assert(b); tbuf_ensure(b, count); @@ -149,16 +142,6 @@ tbuf_reset(struct tbuf *b) b->len = 0; } -void -tbuf_append(struct tbuf *b, const void *data, size_t len) -{ - tbuf_assert(b); - tbuf_ensure(b, len + 1); - memcpy(b->data + b->len, data, len); - b->len += len; - *(((char *)b->data) + b->len) = '\0'; -} - void tbuf_append_field(struct tbuf *b, void *f) { diff --git a/core/util.c b/core/util.c index b86ee7a785c3afc0d766021e85a9ba769fededa0..0c7ba4eec72be7d723af399d7cf49a689262634d 100644 --- a/core/util.c +++ b/core/util.c @@ -125,15 +125,14 @@ print_trace(FILE *f) } fprintf(f, "backtrace:\n"); while (stack_bottom <= (void *)frame && (void *)frame < stack_top) { - fprintf(f, " - { frame: %p, pc: %p }\n", - frame + 2 * sizeof(void *), frame->ret); + fprintf(f, " - { frame: %p, pc: %p }\n", frame + 2 * sizeof(void *), frame->ret); frame = frame->rbp; } } #endif -void __attribute__((noreturn)) -assert_fail(const char *assertion, const char *file, unsigned int line, const char *function) +void __attribute__ ((noreturn)) + assert_fail(const char *assertion, const char *file, unsigned int line, const char *function) { fprintf(stderr, "%s:%i: %s: assertion %s failed.\n", file, line, function, assertion); #if CORO_ASM diff --git a/include/bert.h b/include/bert.h deleted file mode 100644 index fc9c7b6294bd44cf8d6ec67d991ee3328a43edf6..0000000000000000000000000000000000000000 --- a/include/bert.h +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright (C) 2010 Mail.RU - * Copyright (C) 2010 Yuriy Vostrikov - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef TARANTOOL_BERT_H -#define TARANTOOL_BERT_H - -#include <arpa/inet.h> -#include <string.h> -#include <stdbool.h> - -#include <tbuf.h> -#include <util.h> - -#define ERL_VERSION 131 -#define ERL_SMALL_INT 97 -#define ERL_INT 98 -#define ERL_SMALL_BIGNUM 110 -#define ERL_LARGE_BIGNUM 111 -#define ERL_FLOAT 99 -#define ERL_ATOM 100 -#define ERL_SMALL_TUPLE 104 -#define ERL_LARGE_TUPLE 105 -#define ERL_NIL 106 -#define ERL_STRING 107 -#define ERL_LIST 108 -#define ERL_BIN 109 - -extern struct tbuf bert_saved_state, bert_last_packet; - -char *bert_sprint(struct tbuf *b); - -static inline void bert_save_state(struct tbuf *b) -{ - memcpy(&bert_saved_state, b, sizeof(*b)); -} - -static inline void bert_restore_state(struct tbuf *b) -{ - memcpy(b, &bert_saved_state, sizeof(*b)); -} - -static inline void bert_take_bytes(struct tbuf *b, size_t n) -{ - b->len -= n; - b->size -= n; - b->data += n; -} - -#define bert_check_bytes(b, n) if (b->len < n) goto bert_match_failure; - -#define bert_peek(b, type, size) \ - ({ \ - bert_check_bytes(b, size); \ - type *p = b->data; \ - bert_take_bytes(b, size); \ - p; \ - }) - -#define bert_match_prim(b, v, fail, type) \ - ({ \ - bool r = 0; \ - bert_check_bytes(b, sizeof(type)); \ - type *p = b->data; \ - if (*p != v) { \ - fail; \ - } else { \ - r = 1; \ - bert_take_bytes(b, sizeof(type)); \ - } \ - r; \ - }) - -#define bert_peek_u8(b) *bert_peek(b, uint8_t, sizeof(uint8_t)) -#define bert_peek_n16(b) ntohs(*bert_peek(b, uint16_t, sizeof(uint16_t))) -#define bert_peek_n32(b) ntohl(*bert_peek(b, uint32_t, sizeof(uint32_t))) -#define bert_peek_bytes(b, n) bert_peek(b, uint8_t, n) - -#define bert_match_u8(b, v) bert_match_prim(b, v, goto bert_match_failure, uint8_t) -#define bert_match_n16(b, v) bert_match_prim(b, htons(v), goto bert_match_failure, uint16_t) -#define bert_match_n32(b, v) bert_match_prim(b, htonl(v), goto bert_match_failure, uint32_t) - -#define bert_cmp_u8(b, v) bert_match_prim(b, v, , uint8_t) -#define bert_cmp_n16(b, v) bert_match_prim(b, htons(v), , uint16_t) -#define bert_cmp_n32(b, v) bert_match_prim(b, htonl(v), , uint32_t) - -#define bert_match_header(b) \ - ({ \ - bert_match_u8(b, ERL_VERSION); \ - memcpy(&bert_last_packet, b, sizeof(*b)); \ - }) - -#define bert_cmp_atom(b, v) \ - ({ \ - bert_save_state(b); \ - bert_match_u8(b, ERL_ATOM); \ - size_t atom_len = bert_peek_n16(b); \ - uint8_t *atom = bert_peek_bytes(b, atom_len); \ - bool r = (strlen(v) == atom_len && \ - memcmp(atom, v, atom_len) == 0); \ - if (!r) bert_restore_state(b); \ - r; \ - }) - -#define bert_match_atom(b, v) \ - ({ \ - bert_match_u8(b, ERL_ATOM); \ - size_t atom_len = bert_peek_n16(b); \ - uint8_t *atom = bert_peek_bytes(b, atom_len); \ - if (strlen(v) != atom_len && \ - memcmp(atom, v, atom_len) != 0) \ - goto bert_match_failure; \ - }) - -#define bert_peek_int(b) \ - ({ \ - int64_t r = 0; \ - uint8_t tag; \ - tag = bert_peek_u8(b); \ - switch (tag) { \ - case ERL_SMALL_INT: r = bert_peek_u8(b); break; \ - case ERL_INT: r = bert_peek_n32(b); break; \ - case ERL_SMALL_BIGNUM: { \ - uint8_t n = bert_peek_u8(b); \ - uint8_t sign = bert_peek_u8(b); \ - uint8_t *bytes = bert_peek_bytes(b, n); \ - if (n > 8) goto bert_match_failure; \ - for(int i = 0; i < n; i++) \ - r += ((int64_t)bytes[i] << (i * 8)); \ - if (sign) r = -r; \ - break; \ - } \ - default: goto bert_match_failure; \ - } \ - r; \ - }) - -#define bert_peek_string(b) \ - ({ \ - match_u8(b, ERL_STRING); \ - int len = peek_n16(b); \ - tbuf_peek(p, len); \ - }) - -#define bert_peek_bin(b) \ - ({ \ - bert_match_u8(b, ERL_BIN); \ - int len = bert_peek_n16(b); \ - struct tbuf *r = tbuf_alloc(b->pool, NULL, 0); \ - r->len = r->size = len; \ - r->data = bert_peek_bytes(b, len); \ - r; \ - }) - -#define bert_peek_tuple(b) \ - ({ \ - uint32_t arity; \ - uint8_t tag; \ - tag = bert_peek_u8(b); \ - switch (tag) { \ - case ERL_SMALL_TUPLE: arity = bert_peek_u8(b); break; \ - case ERL_LARGE_TUPLE: arity = bert_peek_n32(b); break; \ - default: goto bert_match_failure; \ - } \ - arity; \ - }) - -#define bert_match_tuple(b, a) \ - ({ \ - uint32_t arity = bert_peek_tuple(b); \ - if (arity != a) goto bert_match_failure; \ - }) - -static inline void bert_pack_u8(struct tbuf *b, uint8_t v) -{ - size_t o = tbuf_reserve(b, sizeof(uint8_t)); - *(uint8_t *) (b->data + o) = v; -} - -static inline void bert_pack_n16(struct tbuf *b, uint16_t v) -{ - size_t o = tbuf_reserve(b, sizeof(uint16_t)); - *(uint16_t *) (b->data + o) = htons(v); -} - -static inline void bert_pack_n32(struct tbuf *b, uint32_t v) -{ - size_t o = tbuf_reserve(b, sizeof(uint32_t)); - *(uint32_t *) (b->data + o) = htonl(v); -} - -static inline void bert_pack_header(struct tbuf *b) -{ - bert_pack_u8(b, ERL_VERSION); -} - -static inline void bert_pack_tuple(struct tbuf *b, uint8_t arity) -{ - bert_pack_u8(b, ERL_SMALL_TUPLE); - bert_pack_u8(b, arity); -} - -#define bert_pack_atom(b, str) bert_pack_atom_((b), (str), strlen((str))) -static inline void bert_pack_atom_(struct tbuf *b, const char *atom, size_t atom_len) -{ - bert_pack_u8(b, ERL_ATOM); - bert_pack_n16(b, atom_len); - size_t offt = tbuf_reserve(b, atom_len); - memcpy(b->data + offt, atom, atom_len); -} - -static inline void bert_pack_int(struct tbuf *b, int64_t v) -{ - if (0 <= v && v <= 255) { - bert_pack_u8(b, ERL_SMALL_INT); - bert_pack_u8(b, (uint8_t) v); - return; - } - - if (-(1 << 27) <= v && v <= (1 << 27) - 1) { - bert_pack_u8(b, ERL_INT); - bert_pack_n32(b, v); - return; - } - - bert_pack_u8(b, ERL_SMALL_BIGNUM); - size_t n = tbuf_reserve(b, sizeof(uint8_t)); - *(uint8_t *) (b->data + n) = 0; - bert_pack_u8(b, v < 0); - if (v < 0) - v = -v; - while (v) { - size_t o = tbuf_reserve(b, sizeof(uint8_t)); - *(uint8_t *) (b->data + o) = v; - v >>= 8; - (*(uint8_t *) (b->data + n))++; - } -} - -static inline void bert_pack_bin(struct tbuf *b, const struct tbuf *v) -{ - bert_pack_u8(b, ERL_BIN); - bert_pack_n16(b, v->len); - size_t o = tbuf_reserve(b, v->len); - memcpy(b->data + o, v->data, v->len); -} - -#define bert_panic(msg) panic(msg "can't parse bert packet: %s", bert_sprint(&bert_last_packet)) -#endif diff --git a/include/coro.h b/include/coro.h index bcbca266165eacf9aae6f70882368a05d110270e..32e46a0052b2aae954bbe9d7860a31b918a48664 100644 --- a/include/coro.h +++ b/include/coro.h @@ -36,8 +36,8 @@ struct tarantool_coro { size_t stack_size; }; -struct tarantool_coro * -tarantool_coro_create(struct tarantool_coro *ctx, void (*f)(void *), void *data); +struct tarantool_coro *tarantool_coro_create(struct tarantool_coro *ctx, void (*f) (void *), + void *data); void tarantool_coro_destroy(struct tarantool_coro *ctx); #endif diff --git a/include/fiber.h b/include/fiber.h index 2e9bb53eb40613e7b9b0e32e86650c318be00850..63e21ad58a2a8441908b3048eecf392cc76cd206 100644 --- a/include/fiber.h +++ b/include/fiber.h @@ -34,7 +34,6 @@ #include <netinet/in.h> #include <arpa/inet.h> - #include <tarantool_ev.h> #include <palloc.h> #include <tbuf.h> @@ -69,7 +68,7 @@ struct fiber { struct tbuf *rbuf; struct tbuf *cleanup; - SLIST_ENTRY(fiber) link, zombie_link; + SLIST_ENTRY(fiber) link, zombie_link; struct ring *inbox; @@ -82,6 +81,7 @@ struct fiber { void *data; + u64 cookie; bool has_peer; char peer_name[32]; bool reading_inbox; @@ -121,9 +121,7 @@ void raise_(int); struct msg *read_inbox(void); int fiber_bread(struct tbuf *, size_t v); - -inline static void -add_iov_unsafe(void *buf, size_t len) +inline static void add_iov_unsafe(void *buf, size_t len) { struct iovec *v; assert(fiber->iov->size - fiber->iov->len >= sizeof(*v)); @@ -134,14 +132,12 @@ add_iov_unsafe(void *buf, size_t len) fiber->iov_cnt++; } -inline static void -iov_ensure(size_t count) +inline static void iov_ensure(size_t count) { tbuf_ensure(fiber->iov, sizeof(struct iovec) * count); } -inline static void -add_iov(void *buf, size_t len) +inline static void add_iov(void *buf, size_t len) { iov_ensure(1); add_iov_unsafe(buf, len); @@ -149,8 +145,8 @@ add_iov(void *buf, size_t len) void add_iov_dup(void *buf, size_t len); bool write_inbox(struct fiber *recipient, struct tbuf *msg); -int inbox_size(struct fiber * recipient); -void wait_inbox(struct fiber * recipient); +int inbox_size(struct fiber *recipient); +void wait_inbox(struct fiber *recipient); char *fiber_peer_name(struct fiber *fiber); ssize_t fiber_read(void *buf, size_t count); @@ -170,12 +166,11 @@ typedef enum fiber_server_type { udp_server } fiber_server_type; -struct fiber *fiber_server(fiber_server_type type, int port, void (*handler)(void *), void *, - void (*on_bind)(void *)); +struct fiber *fiber_server(fiber_server_type type, int port, void (*handler) (void *), void *, + void (*on_bind) (void *)); struct child *spawn_child(const char *name, int inbox_size, - struct tbuf *(*handler) (void *, struct tbuf *), - void *state); + struct tbuf *(*handler) (void *, struct tbuf *), void *state); #endif diff --git a/include/iproto.h b/include/iproto.h index 05c16a08f8a021c115a898b21236fa7bf2ffe467..1da28f79f8a6900edc182847e7b7e7a85cc0cff9 100644 --- a/include/iproto.h +++ b/include/iproto.h @@ -34,7 +34,7 @@ static inline struct iproto_header *iproto(const struct tbuf *t) struct iproto_interactor; struct iproto_interactor -*iproto_interactor(uint32_t(*interact) (uint32_t msg, uint8_t * data, size_t len)); +*iproto_interactor(uint32_t (*interact) (uint32_t msg, uint8_t *data, size_t len)); void iproto_interact(void *); @@ -64,8 +64,12 @@ void iproto_interact(void *); _(ERR_CODE_NOTHING, 0x00002400) /* nothing to do (not an error) */ \ _(ERR_CODE_UPDATE_ID, 0x00002502) /* id's update */ \ _(ERR_CODE_WRONG_VERSION, 0x00002602) /* unsupported version of protocol */ \ - _(ERR_CODE_UNKNOWN_ERROR, 0x00002702) + /* other generic error codes */ \ + _(ERR_CODE_UNKNOWN_ERROR, 0x00002702) \ + _(ERR_CODE_NODE_NOT_FOUND, 0x00003102) \ + _(ERR_CODE_NODE_FOUND, 0x00003702) \ + _(ERR_CODE_INDEX_VIOLATION, 0x00003802) ENUM(error_codes, ERROR_CODES); -extern const char *error_codes_strs[]; +extern char *error_codes_strs[]; #endif diff --git a/include/log_io.h b/include/log_io.h index f643e33a86a59c28c3c9f2f46ceead2ae6c66342..58df0173457282de1e1eb06a826225f541a71da5 100644 --- a/include/log_io.h +++ b/include/log_io.h @@ -37,59 +37,110 @@ #define RECOVER_READONLY 1 -struct log_io; +extern const u16 wal_tag, snap_tag; +extern const u64 default_cookie; +extern const u32 default_version; + struct recovery_state; -typedef int (*row_handler) (struct recovery_state *, const struct tbuf *); -typedef struct tbuf *(*row_reader) (FILE * f, struct palloc_pool * pool); +typedef int (row_handler) (struct recovery_state *, struct tbuf *); +typedef struct tbuf *(row_reader) (FILE *f, struct palloc_pool *pool); + +enum log_mode { + LOG_READ, + LOG_WRITE +}; + +struct log_io_class { + row_reader *reader; + u64 marker, eof_marker; + size_t marker_size, eof_marker_size; + size_t rows_per_file; + double fsync_delay; + bool panic_if_error; + + const char *filetype; + const char *version; + const char *suffix; + const char *dirname; +}; + +struct log_io { + struct log_io_class *class; + FILE *f; + + ev_stat stat; + enum log_mode mode; + size_t rows; + size_t retry; + char filename[PATH_MAX + 1]; +}; + +struct recovery_state { + i64 lsn, confirmed_lsn; + + struct log_io *current_wal; /* the WAL we'r currently reading/writing from/to */ + struct log_io_class **snap_class, **wal_class, *snap_prefered_class, *wal_prefered_class; + struct child *wal_writer; + + /* row_handler will be presented by most recent format of data + log_io_class->reader is responsible of converting data from old format */ + row_handler *row_handler; + + ev_timer wal_timer; + ev_tstamp recovery_lag, recovery_last_update_tstamp; -struct row_v04 { - i64 lsn; /* this used to be tid */ - u16 type; + int snap_io_rate_limit; + u64 cookie; + + /* pointer to user supplied custom data */ + void *data; +}; + +struct wal_write_request { + i64 lsn; u32 len; u8 data[]; } __packed__; -struct row_v05 { +struct row_v11 { + u32 header_crc32c; i64 lsn; double tm; u32 len; u32 data_crc32c; - u32 header_crc32c; u8 data[]; } __packed__; -static inline struct row_v04 *row_v04(const struct tbuf *t) +static inline struct row_v11 *row_v11(const struct tbuf *t) { - return (struct row_v04 *)t->data; + return (struct row_v11 *)t->data; } -static inline struct row_v05 *row_v05(const struct tbuf *t) -{ - return (struct row_v05 *)t->data; -} +struct tbuf *convert_to_v11(struct tbuf *orig, u16 tag, u64 cookie, i64 lsn); struct recovery_state *recover_init(const char *snap_dirname, const char *xlog_dirname, - row_reader snap_row_reader, row_handler snap_row_handler, row_handler xlog_row_handler, - int rows_per_file, double fsync_delay, double snap_io_rate_limit, - int inbox_size, int flags, void *data); + row_reader snap_row_reader, row_handler row_handler, + int rows_per_file, double fsync_delay, int inbox_size, + int flags, void *data); int recover(struct recovery_state *, i64 lsn); void recover_follow(struct recovery_state *r, ev_tstamp wal_dir_rescan_delay); void recover_finalize(struct recovery_state *r); -bool wal_write_v04(struct recovery_state *r, int op, const u8 * data, size_t len); +bool wal_write(struct recovery_state *r, u16 tag, u64 cookie, i64 lsn, struct tbuf *data); + +void recovery_setup_panic(struct recovery_state *r, bool on_snap_error, bool on_wal_error); -/* recovery accessors */ -struct palloc_pool *recovery_pool(struct recovery_state *r); int confirm_lsn(struct recovery_state *r, i64 lsn); -int64_t confirmed_lsn(struct recovery_state *r); int64_t next_lsn(struct recovery_state *r, i64 new_lsn); -struct child * wal_writer(struct recovery_state *r); int read_log(const char *filename, row_reader reader, row_handler xlog_handler, row_handler snap_handler, void *state); -struct fiber *recover_follow_remote(struct recovery_state *r, char *ip_addr, int port); +int default_remote_row_handler(struct recovery_state *r, struct tbuf *row); +struct fiber *recover_follow_remote(struct recovery_state *r, char *ip_addr, int port, + int (*handler) (struct recovery_state *r, struct tbuf *row)); struct log_io_iter; -void snapshot_write_row(struct log_io_iter *i, struct tbuf *row); -void snapshot_save(struct recovery_state *r, void (*loop)(struct log_io_iter *)); +void snapshot_write_row(struct log_io_iter *i, u16 tag, u64 cookie, struct tbuf *row); +void snapshot_save(struct recovery_state *r, void (*loop) (struct log_io_iter *)); + #endif diff --git a/include/nbuf.h b/include/nbuf.h deleted file mode 100644 index b41023ca7c765c058b4eb5e3a6106bd0b49e5fa1..0000000000000000000000000000000000000000 --- a/include/nbuf.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2010 Mail.RU - * Copyright (C) 2010 Yuriy Vostrikov - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef TARANTOOL_NBUF_H -#define TARANTOOL_NBUF_H - -#include <stdint.h> - -#include <palloc.h> -#include <tbuf.h> - -struct nbuf; -struct nbuf *nbuf_alloc(struct palloc_pool *pool, uint32_t owner, uint32_t initial_size); -void nbuf_prepend_tbuf(struct nbuf *buf, struct tbuf *t); -void *nbuf_promise(struct nbuf *buf, uint32_t size); -void nbuf_confirm(struct nbuf *buf, uint32_t size); -void *nbuf_request(struct nbuf *buf, uint32_t size); - -#endif diff --git a/include/palloc.h b/include/palloc.h index 10fcf049fbb3e69909e8c4e6a5815000155fcba9..b33a4724cc12fc3394b34e0529246fb7e77d0d95 100644 --- a/include/palloc.h +++ b/include/palloc.h @@ -32,20 +32,22 @@ #include <stdint.h> #include <tbuf.h> +#include <util.h> #include <third_party/queue.h> struct palloc_pool; extern struct palloc_pool *eter_pool; int palloc_init(void); -void *palloc(struct palloc_pool *pool, size_t size); -void *p0alloc(struct palloc_pool *pool, size_t size); +void *palloc(struct palloc_pool *pool, size_t size) __regparm2__; +void *p0alloc(struct palloc_pool *pool, size_t size) __regparm2__; void *palloca(struct palloc_pool *pool, size_t size, size_t align); void prelease(struct palloc_pool *pool); +void prelease_after(struct palloc_pool *pool, size_t after); struct palloc_pool *palloc_create_pool2(const char *name, size_t initial_size); struct palloc_pool *palloc_create_pool(const char *name); void palloc_destroy(struct palloc_pool *); const char *palloc_name(struct palloc_pool *, const char *); -size_t palloc_greatest_size(void); +size_t palloc_allocated(struct palloc_pool *); void palloc_stat(struct tbuf *buf); diff --git a/include/pickle.h b/include/pickle.h index 8962b6c4a71ed550f69d2829fb09da9fa6c9b04e..fd98135bab5a4b23dd02a9d15bcc4ec2612a96c8 100644 --- a/include/pickle.h +++ b/include/pickle.h @@ -33,7 +33,10 @@ u8 *save_varint32(u8 *target, u32 value); void write_varint32(struct tbuf *b, u32 value); u8 read_u8(struct tbuf *b); +u16 read_u16(struct tbuf *b); u32 read_u32(struct tbuf *b); +u64 read_u64(struct tbuf *b); + u32 read_varint32(struct tbuf *buf); void *read_field(struct tbuf *buf); @@ -43,30 +46,28 @@ u32 valid_tuple(struct tbuf *buf, u32 cardinality); size_t varint32_sizeof(u32); - -inline static u32 -load_varint32(void **data) +inline static u32 load_varint32(void **data) { u8 *b = *data; - if(!(b[0] & 0x80)) { + if (!(b[0] & 0x80)) { *data += 1; return (b[0] & 0x7f); } - if(!(b[1] & 0x80)) { + if (!(b[1] & 0x80)) { *data += 2; return (b[0] & 0x7f) << 7 | (b[1] & 0x7f); } - if(!(b[2] & 0x80)) { + if (!(b[2] & 0x80)) { *data += 3; return (b[0] & 0x7f) << 14 | (b[1] & 0x7f) << 7 | (b[2] & 0x7f); } - if(!(b[3] & 0x80)) { + if (!(b[3] & 0x80)) { *data += 4; return (b[0] & 0x7f) << 21 | (b[1] & 0x7f) << 14 | (b[2] & 0x7f) << 7 | (b[3] & 0x7f); } - if(!(b[4] & 0x80)) { + if (!(b[4] & 0x80)) { *data += 5; return (b[0] & 0x7f) << 28 | (b[1] & 0x7f) << 21 | (b[2] & 0x7f) << 14 | (b[3] & 0x7f) << 7 | (b[4] & 0x7f); @@ -76,5 +77,4 @@ load_varint32(void **data) return 0; } - #endif diff --git a/include/say.h b/include/say.h index f1c5672b9b3a639ab74a4953ca3e117b93a4747b..41f9ac7f2b8ad8e1683370a091cb6b5850ccfa7b 100644 --- a/include/say.h +++ b/include/say.h @@ -46,9 +46,9 @@ extern int sayfd; void say_logger_init(int nonblock); void vsay(int level, const char *error, const char *format, va_list ap) - __attribute__ ((format(FORMAT_PRINTF, 3, 0))); + __attribute__ ((format(FORMAT_PRINTF, 3, 0))); void _say(int level, const char *error, const char *format, ...) - __attribute__ ((format(FORMAT_PRINTF, 3, 4))); + __attribute__ ((format(FORMAT_PRINTF, 3, 4))); #define say(level, ...) ({ if(cfg.log_level >= level) _say(level, __VA_ARGS__); }) diff --git a/include/stat.h b/include/stat.h index 902da056bf5f1b922578138ed0a14d0071e56046..85f93774f85e9da37850066c961a4bc8262eba09 100644 --- a/include/stat.h +++ b/include/stat.h @@ -30,7 +30,8 @@ #include <tbuf.h> void stat_init(void); -void stat_collect(const char *key, int value); +int stat_register(char **name, size_t count); +void stat_collect(int base, int name, i64 value); void stat_print(struct tbuf *buf); #endif diff --git a/include/tarantool.h b/include/tarantool.h index f8d4e70eebab84fc1f068874afd084e6c2a1e203..ff8600b240be793c9186822d11c136329e4c65be 100644 --- a/include/tarantool.h +++ b/include/tarantool.h @@ -32,12 +32,12 @@ #include <log_io.h> #include TARANTOOL_CONFIG - struct recovery_state *recovery_state; -void mod_init (void); +void mod_init(void); int mod_cat(const char *filename); void mod_snapshot(struct log_io_iter *); void mod_info(struct tbuf *out); +void mod_exec(char *str, int len, struct tbuf *out); extern struct tarantool_module module; extern struct tarantool_cfg cfg; diff --git a/include/tbuf.h b/include/tbuf.h index dc2a69fc5b6eb236f9a71bc41cf19fe091c66022..729a8494dc59a550910a5b7460f50c4b937c2dd7 100644 --- a/include/tbuf.h +++ b/include/tbuf.h @@ -28,6 +28,7 @@ #define TARANTOOL_TBUF_H #include <stddef.h> +#include <string.h> #include <util.h> @@ -48,6 +49,14 @@ static inline void tbuf_ensure(struct tbuf *e, size_t required) tbuf_ensure_resize(e, required); } +static inline void tbuf_append(struct tbuf *b, const void *data, size_t len) +{ + tbuf_ensure(b, len + 1); + memcpy(b->data + b->len, data, len); + b->len += len; + *(((char *)b->data) + b->len) = '\0'; +} + struct tbuf *tbuf_clone(struct palloc_pool *pool, const struct tbuf *orig); struct tbuf *tbuf_split(struct tbuf *e, size_t at); size_t tbuf_reserve(struct tbuf *b, size_t count); diff --git a/include/util.h b/include/util.h index 938e7eb37a77370eb1d7db6ec21f29d2c9ca5a98..bc7e6f5370cf8a21793016ffcd03b56baf2096be 100644 --- a/include/util.h +++ b/include/util.h @@ -39,7 +39,7 @@ #define ENUM_STRS_MEMBER(s, v) [s] = #s, #define ENUM(enum_name, enum_members) enum enum_name {enum_members(ENUM_MEMBER) enum_name##_MAX} #define STRS(enum_name, enum_members) \ - const char *enum_name##_strs[enum_name##_MAX + 1] = {enum_members(ENUM_STRS_MEMBER) '\0'} + char *enum_name##_strs[enum_name##_MAX + 1] = {enum_members(ENUM_STRS_MEMBER) '\0'} // Macros for printf functions #include <inttypes.h> @@ -47,10 +47,12 @@ #define PRI_SZ "lu" #define PRI_SSZ "ld" #define PRI_OFFT "lu" +#define PRI_XFFT "lx" #else #define PRI_SZ "u" #define PRI_SSZ "d" #define PRI_OFFT "llu" +#define PRI_XFFT "llx" #endif #define nelem(x) (sizeof((x))/sizeof((x)[0])) @@ -72,19 +74,20 @@ #ifndef TYPEALIGN #define TYPEALIGN(ALIGNVAL,LEN) \ - (((long) (LEN) + ((ALIGNVAL) - 1)) & ~((long) ((ALIGNVAL) - 1))) + (((uintptr_t) (LEN) + ((ALIGNVAL) - 1)) & ~((uintptr_t) ((ALIGNVAL) - 1))) #define SHORTALIGN(LEN) TYPEALIGN(sizeof(int16_t), (LEN)) #define INTALIGN(LEN) TYPEALIGN(sizeof(int32_t), (LEN)) #define MAXALIGN(LEN) TYPEALIGN(sizeof(int64_t), (LEN)) #define PTRALIGN(LEN) TYPEALIGN(sizeof(void*), (LEN)) +#define CACHEALIGN(LEN) TYPEALIGN(32, (LEN)) #endif - #define __packed__ __attribute__((packed)) #define __noinline__ __attribute__((noinline)) #define __unused__ __attribute__((unused)) #define __cleanup__(f) __attribute__((cleanup (f))) +#define __regparm2__ __attribute__((regparm(2))) typedef uint8_t u8; typedef uint16_t u16; @@ -109,13 +112,11 @@ void *xrealloc(void *ptr, size_t size); void __gcov_flush(); - struct frame { struct frame *rbp; void *ret; }; - void save_rbp(void **rbp); extern void *main_stack_frame; @@ -124,7 +125,7 @@ extern void *main_stack_frame; #else # define assert(pred) ((pred) ? (void)(0) : assert_fail (#pred, __FILE__, __LINE__, __FUNCTION__)) void assert_fail(const char *assertion, const char *file, - unsigned int line, const char *function) __attribute__((noreturn)); + unsigned int line, const char *function) __attribute__ ((noreturn)); #endif #endif diff --git a/mod/feeder/feeder.c b/mod/feeder/feeder.c index 9fd430938068187ede97a9b8d70c21e8ec18b3f0..16957dee7e488655dddc7d3b84364ef9821824e3 100644 --- a/mod/feeder/feeder.c +++ b/mod/feeder/feeder.c @@ -31,8 +31,10 @@ #include <fiber.h> #include <util.h> +static char *custom_proc_title; + static int -send_row(struct recovery_state *r __unused__, const struct tbuf *t) +send_row(struct recovery_state *r __unused__, struct tbuf *t) { u8 *data = t->data; ssize_t bytes, len = t->len; @@ -46,7 +48,7 @@ send_row(struct recovery_state *r __unused__, const struct tbuf *t) data += bytes; } - say_debug("send row: %"PRIu32" bytes %s", t->len, tbuf_to_hex(t)); + say_debug("send row: %" PRIu32 " bytes %s", t->len, tbuf_to_hex(t)); return 0; } @@ -55,13 +57,14 @@ static void recover_feed_slave(int sock) { struct recovery_state *log_io; + struct tbuf *ver; i64 lsn; ssize_t r; fiber->has_peer = true; fiber->fd = sock; fiber->name = "feeder"; - set_proc_title("feeder:client_handler %s", fiber_peer_name(fiber)); + set_proc_title("feeder:client_handler%s %s", custom_proc_title, fiber_peer_name(fiber)); ev_default_loop(0); @@ -72,17 +75,18 @@ recover_feed_slave(int sock) exit(EXIT_SUCCESS); } + ver = tbuf_alloc(fiber->pool); + tbuf_append(ver, &default_version, sizeof(default_version)); + send_row(NULL, ver); + log_io = recover_init(NULL, cfg.wal_feeder_dir, - NULL, NULL, send_row, - 0, 0, 0, - 64, RECOVER_READONLY, false); + NULL, send_row, 0, 0, 64, RECOVER_READONLY, false); recover(log_io, lsn); recover_follow(log_io, 0.1); ev_loop(0); } - void mod_init(void) { @@ -96,7 +100,16 @@ mod_init(void) if (cfg.wal_feeder_dir == NULL) panic("can't start feeder without wal_feeder_dir"); - set_proc_title("feeder:acceptor %s:%i", + if (cfg.custom_proc_title == NULL) + custom_proc_title = ""; + else { + custom_proc_title = palloc(eter_pool, strlen(cfg.custom_proc_title) + 2); + strcat(custom_proc_title, "@"); + strcat(custom_proc_title, cfg.custom_proc_title); + } + + set_proc_title("feeder:acceptor%s %s:%i", + custom_proc_title, cfg.wal_feeder_bind_ipaddr == NULL ? "ANY" : cfg.wal_feeder_bind_ipaddr, cfg.wal_feeder_bind_port); @@ -123,7 +136,7 @@ mod_init(void) goto exit; } - if (bind(server, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) { + if (bind(server, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { say_syserror("bind"); goto exit; } @@ -144,7 +157,15 @@ mod_init(void) } if (child == 0) recover_feed_slave(client); + else + close(client); } -exit: + exit: exit(EXIT_FAILURE); } + +void +mod_exec(char *str __unused__, int len __unused__, struct tbuf *out) +{ + tbuf_printf(out, "Unimplemented"); +} diff --git a/mod/feeder/feeder_cfg.cfg_tmpl b/mod/feeder/feeder_cfg.cfg_tmpl index ce82d4905d66bfd6bc6d9419e34a46d680e3fcf6..41b2d40b31e9e519e0333a33b7e605b24cb2581b 100644 --- a/mod/feeder/feeder_cfg.cfg_tmpl +++ b/mod/feeder/feeder_cfg.cfg_tmpl @@ -6,3 +6,6 @@ wal_feeder_bind_port=0 # Directory with WAL files to serve wal_feeder_dir=NULL + +# custom proc title is appended after normal +custom_proc_title=NULL diff --git a/mod/silverbox/Makefile b/mod/silverbox/Makefile index e1ed00ba11410b5860b82a1397d737cb0e57c023..1a16feb4a728bf62e9de88785c2207bb8a0937e6 100644 --- a/mod/silverbox/Makefile +++ b/mod/silverbox/Makefile @@ -3,6 +3,6 @@ core/tarantool.o: CFLAGS += -DSTORAGE obj += core/admin.o obj += mod/silverbox/box.o obj += mod/silverbox/memcached.o -# obj += third_party/murmur_hash2.o +obj += third_party/qsort_arg.o cfg_tmpl += mod/silverbox/box_cfg.cfg_tmpl diff --git a/mod/silverbox/assoc.h b/mod/silverbox/assoc.h index 26680afc412875632f0e615b30cff49ac8d63310..69e0656a64373033db3be2d2526f3ff838ba8ac1 100644 --- a/mod/silverbox/assoc.h +++ b/mod/silverbox/assoc.h @@ -24,7 +24,9 @@ static inline khint_t __ac_X31_hash_lstr(void *s) khint_t l; l = load_varint32(&s); khint_t h = 0; - if (l) for (; l-- ; s++) h = (h << 5) - h + *(u8 *)s; + if (l) + for (; l--; s++) + h = (h << 5) - h + *(u8 *)s; return h; } @@ -35,7 +37,7 @@ static inline int lstrcmp(void *a, void *b) al = load_varint32(&a); bl = load_varint32(&b); - if(al != bl) + if (al != bl) return bl - al; return memcmp(a, b, al); } @@ -44,12 +46,11 @@ static inline int lstrcmp(void *a, void *b) #define kh_lstr_hash_func(key) ({ void *_k = key; unsigned int l = load_varint32(&_k); MurmurHash2(_k, l, 13); }) #define kh_lstr_hash_equal(a, b) (lstrcmp(a, b) == 0) -KHASH_INIT(lstr2ptr_map, void*, ptr_t, 1, kh_lstr_hash_func, kh_lstr_hash_equal, xrealloc) -KHASH_INIT(ptr_set, uint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal, xrealloc); +KHASH_INIT(lstr2ptr_map, void *, ptr_t, 1, kh_lstr_hash_func, kh_lstr_hash_equal, xrealloc) + KHASH_INIT(ptr_set, uint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal, xrealloc); void assoc_init(void); - #define assoc_clear(hash_name, hash) kh_clear(hash_name, hash) #define assoc_exist(hash_name, hash, key) ({ \ @@ -90,7 +91,6 @@ void assoc_init(void); _k; \ }) - #define assoc_foreach(hash, kiter) \ for (kiter = kh_begin(hash); kiter != kh_end(hash); ++kiter) \ if (kh_exist(hash, kiter)) diff --git a/mod/silverbox/box.c b/mod/silverbox/box.c index d21c088c83477d6649a94af6d6434cde17a26b9c..47a209f0d2c1514a73758c78f6d07a1cd42c79c7 100644 --- a/mod/silverbox/box.c +++ b/mod/silverbox/box.c @@ -39,27 +39,35 @@ #include <tarantool.h> #include <tbuf.h> #include <util.h> +// #include <third_party/sptree.h> #include <mod/silverbox/box.h> bool box_updates_allowed = false; static char *status = "unknown"; +static int stat_base; STRS(messages, MESSAGES); const int MEMCACHED_NAMESPACE = 23; static char *custom_proc_title; -/* hooks */ -typedef int (*box_hook_t)(struct box_txn *txn); +const struct field ASTERISK = { + .len = UINT32_MAX, + { + .data_ptr = NULL, + } +}; +/* hooks */ +typedef int (*box_hook_t) (struct box_txn * txn); struct namespace namespace[256]; struct box_snap_row { - u32 namespace; - u32 tuple_size; - u32 data_size; + u32 namespace; + u32 tuple_size; + u32 data_size; u8 data[]; } __packed__; @@ -69,10 +77,8 @@ box_snap_row(const struct tbuf *t) return (struct box_snap_row *)t->data; } - static void tuple_add_iov(struct box_txn *txn, struct box_tuple *tuple); - box_hook_t *before_commit_update_hook; #define box_raise(n, err) \ @@ -82,13 +88,12 @@ box_hook_t *before_commit_update_hook; raise(n, err); \ }) - static void -run_hooks(struct box_txn *txn, box_hook_t *hook) +run_hooks(struct box_txn *txn, box_hook_t * hook) { if (hook != NULL) { for (int i = 0; hook[i] != NULL; i++) { - int result = (*hook[i])(txn); + int result = (*hook[i]) (txn); if (result != ERR_CODE_OK) box_raise(result, "hook returned error"); } @@ -98,149 +103,219 @@ run_hooks(struct box_txn *txn, box_hook_t *hook) void * next_field(void *f) { - u32 size = load_varint32(&f); - return (u8 *)f + size; + u32 size = load_varint32(&f); + return (u8 *)f + size; } - void * tuple_field(struct box_tuple *tuple, size_t i) { void *field = tuple->data; - if (i >= tuple->cardinality) - return NULL; + if (i >= tuple->cardinality) + return NULL; - while (i-- > 0) - field = next_field(field); + while (i-- > 0) + field = next_field(field); - return field; + return field; } -#define foreach_index(n, index_var) \ - for (struct index *index_var = namespace[(n)].index; \ - index_var->key_position >= 0; \ - index_var++) - -struct box_tuple * -index_find(struct index *index, int key_len, void *key) +bool +field_is_num(void *field) { - return index->find(index, key_len, key); + u32 len = load_varint32(&field); + + if (len == sizeof(u32)) + return true; + + return false; } -void -index_remove(struct index *index, struct box_tuple *tuple) +#define IS_ASTERISK(f) ((f)->len == ASTERISK.len && (f)->data_ptr == ASTERISK.data_ptr) +static i8 +field_compare(struct field *f1, struct field *f2, enum field_data_type type) { - void *key = tuple_field(tuple, index->key_position); - index->remove(index, key); + i8 r; + + if (IS_ASTERISK(f1) || IS_ASTERISK(f2)) + r = 0; + else { + if (type == NUM) { + assert(f1->len == f2->len); + assert(f1->len == sizeof(f1->u32)); + + r = f1->u32 >f2->u32 ? 1 : f1->u32 == f2->u32 ? 0 : -1; + } else { + i32 cmp; + void *f1_data, *f2_data; + + f1_data = f1->len <= sizeof(f1->data) ? f1->data : f1->data_ptr; + f2_data = f2->len <= sizeof(f2->data) ? f2->data : f2->data_ptr; + + cmp = memcmp(f1_data, f2_data, MIN(f1->len, f2->len)); + + if (cmp > 0) + r = 1; + else if (cmp < 0) + r = -1; + else if (f1->len == f2->len) + r = 0; + else if (f1->len > f2->len) + r = 1; + else + r = -1; + } + } + + return r; } -void -index_replace(struct index *index, struct box_tuple *tuple) +/* + * Compare index_tree_members only by fields defined in index->field_cmp_order. + * Return: + * Common meaning: + * < 0 - a is smaler than b + * == 0 - a is equal to b + * > 0 - a is greater than b + * Custom treatment (by absolute value): + * 1 - differ in some key field + * 2 - one tuple is a search pattern + * 3 - differ in pointers + */ +static int +tree_index_member_compare(struct tree_index_member *member_a, struct tree_index_member *member_b, + struct index *index) { - void *key = tuple_field(tuple, index->key_position); - index->replace(index, key, tuple); + i8 r = 0; + + for (i32 i = 0, end = index->key_cardinality; i < end; ++i) { + r = field_compare(&member_a->key[i], &member_b->key[i], index->key_field[i].type); + + if (r != 0) + break; + } + + if (r != 0) + return r; + + if (member_a->tuple == NULL) + return -2; + + if (member_b->tuple == NULL) + return 2; + + if (index->unique == false) { + if (member_a->tuple > member_b->tuple) + return 3; + else if (member_a->tuple < member_b->tuple) + return -3; + } + + return 0; } +#define foreach_index(n, index_var) \ + for (struct index *index_var = namespace[(n)].index; \ + index_var->key_cardinality != 0; \ + index_var++) \ + if (index_var->enabled) static void lock_tuple(struct box_txn *txn, struct box_tuple *tuple) { - if (tuple->flags & WAL_WAIT) + if (tuple->flags & WAL_WAIT) box_raise(ERR_CODE_NODE_IS_RO, "tuple is locked"); say_debug("lock_tuple(%p)", tuple); - txn->lock_tuple = tuple; - tuple->flags |= WAL_WAIT; + txn->lock_tuple = tuple; + tuple->flags |= WAL_WAIT; } - static void unlock_tuples(struct box_txn *txn) { - if (txn->lock_tuple) { - txn->lock_tuple->flags &= ~WAL_WAIT; + if (txn->lock_tuple) { + txn->lock_tuple->flags &= ~WAL_WAIT; txn->lock_tuple = NULL; } } - static void -field_print(struct tbuf *buf, void *f) +field_print(struct tbuf *buf, void *f) { - uint32_t size; + uint32_t size; - size = load_varint32(&f); + size = load_varint32(&f); - if (size == 2) - tbuf_printf(buf, "%i:", *(u16 *)f); + if (size == 2) + tbuf_printf(buf, "%i:", *(u16 *)f); - if (size == 4) - tbuf_printf(buf, "%i:", *(u32 *)f); + if (size == 4) + tbuf_printf(buf, "%i:", *(u32 *)f); - while (size-- > 0) { - if (0x20 <= *(u8 *)f && *(u8 *)f < 0x7f) - tbuf_printf(buf, "%c", *(u8 *)f++); - else - tbuf_printf(buf, "\\x%02X", *(u8 *)f++); - } + while (size-- > 0) { + if (0x20 <= *(u8 *)f && *(u8 *)f < 0x7f) + tbuf_printf(buf, "%c", *(u8 *)f++); + else + tbuf_printf(buf, "\\x%02X", *(u8 *)f++); + } } static void tuple_print(struct tbuf *buf, uint8_t cardinality, void *f) { - tbuf_printf(buf, "<"); + tbuf_printf(buf, "<"); - for(size_t i = 0; i < cardinality; i++, f = next_field(f)) { - tbuf_printf(buf, "\""); - field_print(buf, f); - tbuf_printf(buf, "\""); + for (size_t i = 0; i < cardinality; i++, f = next_field(f)) { + tbuf_printf(buf, "\""); + field_print(buf, f); + tbuf_printf(buf, "\""); - if (likely(i + 1 < cardinality)) - tbuf_printf(buf, ", "); + if (likely(i + 1 < cardinality)) + tbuf_printf(buf, ", "); - } + } - tbuf_printf(buf, ">"); + tbuf_printf(buf, ">"); } - static struct box_tuple * tuple_alloc(size_t size) { - size += sizeof(struct box_tuple); - struct box_tuple *tuple = salloc(size); + struct box_tuple *tuple = salloc(sizeof(struct box_tuple) + size); - if (tuple == NULL) + if (tuple == NULL) box_raise(ERR_CODE_MEMORY_ISSUE, "can't allocate tuple"); - tuple->flags = tuple->refs = 0; + tuple->flags = tuple->refs = 0; tuple->flags |= NEW; - say_debug("tuple_alloc(%zu) = %p", size, tuple); - return tuple; -} + tuple->bsize = size; + say_debug("tuple_alloc(%zu) = %p", size, tuple); + return tuple; +} static void tuple_free(struct box_tuple *tuple) { - say_debug("tuple_free(%p)", tuple); + say_debug("tuple_free(%p)", tuple); assert(tuple->refs == 0); - sfree(tuple); + sfree(tuple); } static void tuple_ref(struct box_tuple *tuple, int count) { assert(tuple->refs + count >= 0); - tuple->refs += count; + tuple->refs += count; if (tuple->refs > 0) tuple->flags &= ~NEW; if (tuple->refs == 0) - tuple_free(tuple); + tuple_free(tuple); } void @@ -251,148 +326,304 @@ tuple_txn_ref(struct box_txn *txn, struct box_tuple *tuple) tuple_ref(tuple, +1); } +static struct box_tuple * +index_find_hash_by_tuple(struct index *self, struct box_tuple *tuple) +{ + void *key = tuple_field(tuple, self->key_field->fieldno); + if (key == NULL) + box_raise(ERR_CODE_ILLEGAL_PARAMS, "invalid tuple, can't find key"); + return self->find(self, key); +} static struct box_tuple * -index_find_hash_num(struct index *self, int key_len, void *key) +index_find_hash_num(struct index *self, void *key) { struct box_tuple *ret = NULL; u32 key_size = load_varint32(&key); u32 num = *(u32 *)key; - if (key_len != 1) - box_raise(ERR_CODE_ILLEGAL_PARAMS, "key must be single valued"); if (key_size != 4) box_raise(ERR_CODE_ILLEGAL_PARAMS, "key is not u32"); - assoc_find(int2ptr_map, self->map.int_map, num, ret); + assoc_find(int2ptr_map, self->idx.int_hash, num, ret); #ifdef DEBUG - say_debug("index_find_hash_num(%i, key:%i, tuple:%p)", self->namespace->n, num, ret); + say_debug("index_find_hash_num(self:%p, key:%i) = %p", self, num, ret); #endif return ret; } static struct box_tuple * -index_find_hash_str(struct index *self, int key_len, void *key) +index_find_hash_str(struct index *self, void *key) { struct box_tuple *ret = NULL; - u32 size; - - if (key_len != 1) - box_raise(ERR_CODE_ILLEGAL_PARAMS, "key must be single valued"); - assoc_find(lstr2ptr_map, self->map.str_map, key, ret); - size = load_varint32(&key); + assoc_find(lstr2ptr_map, self->idx.str_hash, key, ret); #ifdef DEBUG - say_debug("index_find_hash_str(%i, key:(%i)'%.*s', tuple:%p)", self->namespace->n, size, size, (u8 *)key, ret); + u32 size = load_varint32(&key); + say_debug("index_find_hash_str(self:%p, key:(%i)'%.*s') = %p", self, size, size, (u8 *)key, + ret); #endif return ret; } +static struct tree_index_member * +tuple2tree_index_member(struct index *index, + struct box_tuple *tuple, struct tree_index_member **member_p) +{ + struct tree_index_member *member; + void *tuple_data = tuple->data; + + if (member_p == NULL || *member_p == NULL) + member = palloc(fiber->pool, SIZEOF_TREE_INDEX_MEMBER(index)); + else + member = *member_p; + + for (i32 i = 0; i < index->field_cmp_order_cnt; ++i) { + struct field f; + + if (i < tuple->cardinality) { + f.len = load_varint32(&tuple_data); + if (f.len <= sizeof(f.data)) { + memset(f.data, 0, sizeof(f.data)); + memcpy(f.data, tuple_data, f.len); + } else + f.data_ptr = tuple_data; + tuple_data += f.len; + } else + f = ASTERISK; + + if (index->field_cmp_order[i] == -1) + continue; + + member->key[index->field_cmp_order[i]] = f; + } + + member->tuple = tuple; + + if (member_p) + *member_p = member; + + return member; +} + +static struct tree_index_member * +alloc_search_pattern(struct index *index, int key_cardinality, void *key) +{ + struct tree_index_member *pattern = index->search_pattern; + void *key_field = key; + + assert(key_cardinality <= index->key_cardinality); + + for (i32 i = 0; i < index->key_cardinality; ++i) + pattern->key[i] = ASTERISK; + for (int i = 0; i < key_cardinality; i++) { + u32 len; + + len = pattern->key[i].len = load_varint32(&key_field); + if (len <= sizeof(pattern->key[i].data)) { + memset(pattern->key[i].data, 0, sizeof(pattern->key[i].data)); + memcpy(pattern->key[i].data, key_field, len); + } else + pattern->key[i].data_ptr = key_field; + + key_field += len; + } + + pattern->tuple = NULL; + + return pattern; +} + +static struct box_tuple * +index_find_tree(struct index *self, void *key) +{ + struct tree_index_member *member = (struct tree_index_member *)key; + + return sptree_str_t_find(self->idx.tree, member); +} + +static struct box_tuple * +index_find_tree_by_tuple(struct index *self, struct box_tuple *tuple) +{ + struct tree_index_member *member = tuple2tree_index_member(self, tuple, NULL); + + return self->find(self, member); +} + static void -index_remove_hash_num(struct index *self, void *key) +index_remove_hash_num(struct index *self, struct box_tuple *tuple) { + void *key = tuple_field(tuple, self->key_field->fieldno); unsigned int key_size = load_varint32(&key); u32 num = *(u32 *)key; - if(key_size != 4) + if (key_size != 4) box_raise(ERR_CODE_ILLEGAL_PARAMS, "key is not u32"); - assoc_delete(int2ptr_map, self->map.int_map, num); + assoc_delete(int2ptr_map, self->idx.int_hash, num); #ifdef DEBUG - say_debug("index_remove_hash_num(%i, key:%i)", self->namespace->n, num); + say_debug("index_remove_hash_num(self:%p, key:%i)", self, num); #endif } static void -index_remove_hash_str(struct index *self, void *key) +index_remove_hash_str(struct index *self, struct box_tuple *tuple) { - assoc_delete(lstr2ptr_map, self->map.str_map, key); + void *key = tuple_field(tuple, self->key_field->fieldno); + assoc_delete(lstr2ptr_map, self->idx.str_hash, key); #ifdef DEBUG u32 size = load_varint32(&key); - say_debug("index_remove_hash_str(%i, key:'%.*s')", self->namespace->n, size, (u8 *)key); + say_debug("index_remove_hash_str(self:%p, key:'%.*s')", self, size, (u8 *)key); #endif } static void -index_replace_hash_num(struct index *self, void *key, void *value) +index_remove_tree_str(struct index *self, struct box_tuple *tuple) { + struct tree_index_member *member = tuple2tree_index_member(self, tuple, NULL); + sptree_str_t_delete(self->idx.tree, member); +} + +static void +index_replace_hash_num(struct index *self, struct box_tuple *old_tuple, struct box_tuple *tuple) +{ + void *key = tuple_field(tuple, self->key_field->fieldno); u32 key_size = load_varint32(&key); u32 num = *(u32 *)key; - if(key_size != 4) + if (old_tuple != NULL) { + void *old_key = tuple_field(old_tuple, self->key_field->fieldno); + load_varint32(&old_key); + u32 old_num = *(u32 *)old_key; + assoc_delete(int2ptr_map, self->idx.int_hash, old_num); + } + + if (key_size != 4) box_raise(ERR_CODE_ILLEGAL_PARAMS, "key is not u32"); - assoc_replace(int2ptr_map, self->map.int_map, num, value); + assoc_replace(int2ptr_map, self->idx.int_hash, num, tuple); #ifdef DEBUG - say_debug("index_replace_hash_num(%i, key:%i, tuple:%p)", self->namespace->n, num, value); + say_debug("index_replace_hash_num(self:%p, old_tuple:%p, tuple:%p) key:%i", self, old_tuple, + tuple, num); #endif } static void -index_replace_hash_str(struct index *self, void *key, void *value) +index_replace_hash_str(struct index *self, struct box_tuple *old_tuple, struct box_tuple *tuple) { - assoc_replace(lstr2ptr_map, self->map.str_map, key, value); + void *key = tuple_field(tuple, self->key_field->fieldno); + + if (old_tuple != NULL) { + void *old_key = tuple_field(old_tuple, self->key_field->fieldno); + assoc_delete(lstr2ptr_map, self->idx.str_hash, old_key); + } + + assoc_replace(lstr2ptr_map, self->idx.str_hash, key, tuple); #ifdef DEBUG u32 size = load_varint32(&key); - say_debug("index_replace_hash_str(%i, key:'%.*s', tuple:%p)", self->namespace->n, size, (u8 *)key, value); + say_debug("index_replace_hash_str(self:%p, old_tuple:%p, tuple:%p) key:'%.*s'", self, + old_tuple, tuple, size, (u8 *)key); #endif } +static void +index_replace_tree_str(struct index *self, struct box_tuple *old_tuple, struct box_tuple *tuple) +{ + struct tree_index_member *member = tuple2tree_index_member(self, tuple, NULL); + + if (old_tuple) + index_remove_tree_str(self, old_tuple); + sptree_str_t_insert(self->idx.tree, member); +} + +static void +index_iterator_init_tree_str(struct index *self, struct tree_index_member *pattern) +{ + sptree_str_t_iterator_init_set(self->idx.tree, + (struct sptree_str_t_iterator **)&self->iterator, pattern); +} + +static struct box_tuple * +index_iterator_next_tree_str(struct index *self, struct tree_index_member *pattern) +{ + struct tree_index_member *member = + sptree_str_t_iterator_next((struct sptree_str_t_iterator *)self->iterator); + + if (member == NULL) + return NULL; + + i32 r = tree_index_member_compare(pattern, member, self); + if (r == -2) + return member->tuple; + + return NULL; +} + +static void +validate_indeces(struct box_txn *txn) +{ + if (namespace[txn->n].index[1].key_cardinality != 0) { /* there is more then one index */ + foreach_index(txn->n, index) { + for (u32 f = 0; f < index->key_cardinality; ++f) { + void *field; + + if (index->key_field[f].type == STR) + continue; + + field = tuple_field(txn->tuple, index->key_field[f].fieldno); + if (!field_is_num(field)) + box_raise(ERR_CODE_ILLEGAL_PARAMS, "field must be NUM"); + } + if (index->type == TREE && index->unique == false) + /* Don't check non unique indexes */ + continue; + + struct box_tuple *tuple = index->find_by_tuple(index, txn->tuple); + + if (tuple != NULL && tuple != txn->old_tuple) + box_raise(ERR_CODE_INDEX_VIOLATION, "unique index violation"); + } + } +} + static int __noinline__ prepare_replace(struct box_txn *txn, size_t cardinality, struct tbuf *data) { - uint8_t *key; - - assert(data != NULL); + assert(data != NULL); if (cardinality == 0) box_raise(ERR_CODE_ILLEGAL_PARAMS, "cardinality can't be equal to 0"); - if(data->len == 0 || data->len != valid_tuple(data, cardinality)) + if (data->len == 0 || data->len != valid_tuple(data, cardinality)) box_raise(ERR_CODE_ILLEGAL_PARAMS, "tuple encoding error"); - txn->tuple = tuple_alloc(data->len); + txn->tuple = tuple_alloc(data->len); tuple_txn_ref(txn, txn->tuple); - txn->tuple->cardinality = cardinality; - txn->tuple->bsize = data->len; - memcpy(txn->tuple->data, data->data, data->len); - key = tuple_field(txn->tuple, txn->index->key_position); - if(key == NULL) - box_raise(ERR_CODE_ILLEGAL_PARAMS, "invalid tuple: can't find key"); - - txn->old_tuple = index_find(txn->index, 1, key); + txn->tuple->cardinality = cardinality; + memcpy(txn->tuple->data, data->data, data->len); + + txn->old_tuple = txn->index->find_by_tuple(txn->index, txn->tuple); + if (txn->old_tuple != NULL) tuple_txn_ref(txn, txn->old_tuple); - if (namespace[txn->n].index[1].key_position >= 0) { /* there is more then one index */ - foreach_index(txn->n, index) { - void *key = tuple_field(txn->tuple, index->key_position); - if(key == NULL) - box_raise(ERR_CODE_ILLEGAL_PARAMS, "invalid tuple, can't find key"); - - struct box_tuple *tuple = index_find(index, 1, key); - - /* - * tuple referenced by secondary keys - * must be same as tuple referenced by index[0] - * if tuple nonexistent (NULL) - it must be nonexistent in all indeces - */ - if(tuple != txn->old_tuple) { - box_raise(ERR_CODE_ILLEGAL_PARAMS, "index violation"); - } - } - } + if (txn->flags & BOX_ADD && txn->old_tuple != NULL) + box_raise(ERR_CODE_NODE_FOUND, "tuple found"); + + if (txn->flags & BOX_REPLACE && txn->old_tuple == NULL) + box_raise(ERR_CODE_NODE_NOT_FOUND, "tuple not found"); - run_hooks(txn, before_commit_update_hook); + validate_indeces(txn); + run_hooks(txn, before_commit_update_hook); - if (txn->old_tuple != NULL) { + if (txn->old_tuple != NULL) { #ifndef NDEBUG - void *ka, *kb; - ka = tuple_field(txn->tuple, txn->index->key_position); - kb = tuple_field(txn->old_tuple, txn->index->key_position); + void *ka, *kb; + ka = tuple_field(txn->tuple, txn->index->key_field->fieldno); + kb = tuple_field(txn->old_tuple, txn->index->key_field->fieldno); int kal, kab; kal = load_varint32(&ka); kab = load_varint32(&kb); assert(kal == kab && memcmp(ka, kb, kal) == 0); #endif lock_tuple(txn, txn->old_tuple); - } else { + } else { /* * if tuple doesn't exist insert GHOST tuple in indeces * in order to avoid race condition @@ -400,36 +631,36 @@ prepare_replace(struct box_txn *txn, size_t cardinality, struct tbuf *data) */ foreach_index(txn->n, index) - index_replace(index, txn->tuple); + index->replace(index, NULL, txn->tuple); - lock_tuple(txn, txn->tuple); + lock_tuple(txn, txn->tuple); txn->tuple->flags |= GHOST; - } + } - return -1; + return -1; } static void commit_replace(struct box_txn *txn) { - int tuples_affected = 1; + int tuples_affected = 1; - if (txn->old_tuple != NULL) { + if (txn->old_tuple != NULL) { foreach_index(txn->n, index) - index_replace(index, txn->tuple); + index->replace(index, txn->old_tuple, txn->tuple); tuple_ref(txn->old_tuple, -1); - } + } txn->tuple->flags &= ~GHOST; tuple_ref(txn->tuple, +1); - if (!(txn->flags & BOX_QUIET) && !txn->in_recover) { - add_iov_dup(&tuples_affected, sizeof(uint32_t)); + if (!(txn->flags & BOX_QUIET) && !txn->in_recover) { + add_iov_dup(&tuples_affected, sizeof(uint32_t)); - if (txn->flags & BOX_RETURN_TUPLE) - tuple_add_iov(txn, txn->tuple); - } + if (txn->flags & BOX_RETURN_TUPLE) + tuple_add_iov(txn, txn->tuple); + } } static void @@ -437,19 +668,121 @@ rollback_replace(struct box_txn *txn) { say_debug("rollback_replace: txn->tuple:%p", txn->tuple); - if (txn->tuple && txn->tuple->flags & GHOST) { + if (txn->tuple && txn->tuple->flags & GHOST) { foreach_index(txn->n, index) - index_remove(index, txn->tuple); - } + index->remove(index, txn->tuple); + } +} + +static void +do_field_arith(u8 op, struct tbuf *field, void *arg, u32 arg_size) +{ + if (field->len != 4) + box_raise(ERR_CODE_ILLEGAL_PARAMS, "num op on field with length != 4"); + if (arg_size != 4) + box_raise(ERR_CODE_ILLEGAL_PARAMS, "num op with arg not u32"); + + switch (op) { + case 1: + *(i32 *)field->data += *(i32 *)arg; + break; + case 2: + *(u32 *)field->data &= *(u32 *)arg; + break; + case 3: + *(u32 *)field->data ^= *(u32 *)arg; + break; + case 4: + *(u32 *)field->data |= *(u32 *)arg; + break; + } } +static void +do_field_splice(struct tbuf *field, void *args_data, u32 args_data_size) +{ + struct tbuf args = { + .len = args_data_size, + .size = args_data_size, + .data = args_data, + .pool = NULL + }; + struct tbuf *new_field = NULL; + void *offset_field, *length_field, *list_field; + u32 offset_size, length_size, list_size; + i32 offset, length; + u32 noffset, nlength; /* normalized values */ + + new_field = tbuf_alloc(fiber->pool); + + offset_field = read_field(&args); + length_field = read_field(&args); + list_field = read_field(&args); + if (args.len != 0) + box_raise(ERR_CODE_ILLEGAL_PARAMS, "do_field_splice: bad args"); + + offset_size = load_varint32(&offset_field); + if (offset_size == 0) + noffset = 0; + else if (offset_size == sizeof(offset)) { + offset = pick_u32(offset_field, &offset_field); + if (offset < 0) { + if (field->len < -offset) + box_raise(ERR_CODE_ILLEGAL_PARAMS, + "do_field_splice: noffset is negative"); + noffset = offset + field->len; + } else + noffset = offset; + } else + box_raise(ERR_CODE_ILLEGAL_PARAMS, "do_field_splice: bad size of offset field"); + if (noffset > field->len) + noffset = field->len; + + length_size = load_varint32(&length_field); + if (length_size == 0) + nlength = field->len - noffset; + else if (length_size == sizeof(length)) { + if (offset_size == 0) + box_raise(ERR_CODE_ILLEGAL_PARAMS, + "do_field_splice: offset field is empty but length is not"); + + length = pick_u32(length_field, &length_field); + if (length < 0) { + if ((field->len - noffset) < -length) + nlength = 0; + else + nlength = length + field->len - noffset; + } else + nlength = length; + } else + box_raise(ERR_CODE_ILLEGAL_PARAMS, "do_field_splice: bad size of length field"); + if (nlength > (field->len - noffset)) + nlength = field->len - noffset; + + list_size = load_varint32(&list_field); + if (list_size > 0 && length_size == 0) + box_raise(ERR_CODE_ILLEGAL_PARAMS, + "do_field_splice: length field is empty but list is not"); + if (list_size > (UINT32_MAX - (field->len - nlength))) + box_raise(ERR_CODE_ILLEGAL_PARAMS, "do_field_splice: list_size is too long"); + + say_debug("do_field_splice: noffset = %i, nlength = %i, list_size = %u", + noffset, nlength, list_size); + + new_field->len = 0; + tbuf_append(new_field, field->data, noffset); + tbuf_append(new_field, list_field, list_size); + tbuf_append(new_field, field->data + noffset + nlength, field->len - (noffset + nlength)); + + *field = *new_field; +} static int __noinline__ prepare_update_fields(struct box_txn *txn, struct tbuf *data, bool old_format) { - struct tbuf **fields; - void *field; - int i; + struct tbuf **fields; + void *field; + int i; void *key; u32 op_cnt; @@ -461,37 +794,34 @@ prepare_update_fields(struct box_txn *txn, struct tbuf *data, bool old_format) key = read_field(data); op_cnt = read_u32(data); - if(op_cnt > 128) + if (op_cnt > 128) box_raise(ERR_CODE_ILLEGAL_PARAMS, "too many ops"); - if(op_cnt == 0) + if (op_cnt == 0) box_raise(ERR_CODE_ILLEGAL_PARAMS, "no ops"); - if(key == NULL) + if (key == NULL) box_raise(ERR_CODE_ILLEGAL_PARAMS, "invalid key"); - txn->old_tuple = index_find(txn->index, 1, key); - if (txn->old_tuple == NULL) { - if (!txn->in_recover) { - int tuples_affected = 0; - add_iov_dup(&tuples_affected, sizeof(uint32_t)); - } - return ERR_CODE_OK; - } + txn->old_tuple = txn->index->find(txn->index, key); + if (txn->old_tuple == NULL) { + if (!txn->in_recover) { + int tuples_affected = 0; + add_iov_dup(&tuples_affected, sizeof(uint32_t)); + } + return ERR_CODE_OK; + } lock_tuple(txn, txn->old_tuple); fields = palloc(fiber->pool, (txn->old_tuple->cardinality + 1) * sizeof(struct tbuf *)); memset(fields, 0, (txn->old_tuple->cardinality + 1) * sizeof(struct tbuf *)); - for (i = 0, field = (uint8_t *)txn->old_tuple->data; - i < txn->old_tuple->cardinality; - i++) - { + for (i = 0, field = (uint8_t *)txn->old_tuple->data; i < txn->old_tuple->cardinality; i++) { fields[i] = tbuf_alloc(fiber->pool); u32 field_size = load_varint32(&field); tbuf_append(fields[i], field, field_size); field += field_size; - } + } if (old_format) { while (op_cnt-- > 0) { @@ -507,28 +837,26 @@ prepare_update_fields(struct box_txn *txn, struct tbuf *data, bool old_format) xor = read_u32(data); add = read_u32(data); - foreach_index(txn->n, index) { - if(index->key_position == field_no) - box_raise(ERR_CODE_ILLEGAL_PARAMS, "update of indexed field"); - } - - if(field_no >= txn->old_tuple->cardinality) - box_raise(ERR_CODE_ILLEGAL_PARAMS, "update of field beyond tuple cardinality"); + if (field_no >= txn->old_tuple->cardinality) + box_raise(ERR_CODE_ILLEGAL_PARAMS, + "update of field beyond tuple cardinality"); struct tbuf *sptr_field = fields[field_no]; new_field_size = load_varint32(&new_field); if (new_field_size) { - if(and != 0 || xor != 0 || add != 0) - box_raise(ERR_CODE_ILLEGAL_PARAMS, "and or xor or add != 0"); + if (and != 0 || xor != 0 || add != 0) + box_raise(ERR_CODE_ILLEGAL_PARAMS, + "and or xor or add != 0"); tbuf_ensure(sptr_field, new_field_size); sptr_field->len = new_field_size; memcpy(sptr_field->data, new_field, new_field_size); } else { uint32_t *num; - if(sptr_field->len != 4) - box_raise(ERR_CODE_ILLEGAL_PARAMS, "num op on field with length != 4"); - num = (uint32_t *)sptr_field->data; /* FIXME: align && endianes */ + if (sptr_field->len != 4) + box_raise(ERR_CODE_ILLEGAL_PARAMS, + "num op on field with length != 4"); + num = (uint32_t *)sptr_field->data; /* FIXME: align && endianes */ *num &= and; *num ^= xor; @@ -543,19 +871,15 @@ prepare_update_fields(struct box_txn *txn, struct tbuf *data, bool old_format) field_no = read_u32(data); - foreach_index(txn->n, index) { - if(index->key_position == field_no) - box_raise(ERR_CODE_ILLEGAL_PARAMS, "update of indexed field"); - } - - if(field_no >= txn->old_tuple->cardinality) - box_raise(ERR_CODE_ILLEGAL_PARAMS, "update of field beyond tuple cardinality"); + if (field_no >= txn->old_tuple->cardinality) + box_raise(ERR_CODE_ILLEGAL_PARAMS, + "update of field beyond tuple cardinality"); struct tbuf *sptr_field = fields[field_no]; op = read_u8(data); - if (op > 4) - box_raise(ERR_CODE_ILLEGAL_PARAMS, "op is not 0, 1, 2, 3 or 4"); + if (op > 5) + box_raise(ERR_CODE_ILLEGAL_PARAMS, "op is not 0, 1, 2, 3, 4 or 5"); arg = read_field(data); arg_size = load_varint32(&arg); @@ -564,158 +888,179 @@ prepare_update_fields(struct box_txn *txn, struct tbuf *data, bool old_format) sptr_field->len = arg_size; memcpy(sptr_field->data, arg, arg_size); } else { - if (sptr_field->len != 4) - box_raise(ERR_CODE_ILLEGAL_PARAMS, "num op on field with length != 4"); - if (arg_size != 4) - box_raise(ERR_CODE_ILLEGAL_PARAMS, "num op with arg not u32"); - - switch(op) { + switch (op) { case 1: - *(i32 *)sptr_field->data += *(i32 *)arg; - break; case 2: - *(u32 *)sptr_field->data &= *(u32 *)arg; - break; case 3: - *(u32 *)sptr_field->data ^= *(u32 *)arg; - break; case 4: - *(u32 *)sptr_field->data |= *(u32 *)arg; + do_field_arith(op, sptr_field, arg, arg_size); + break; + case 5: + do_field_splice(sptr_field, arg, arg_size); break; } } } } - if(data->len != 0) + if (data->len != 0) box_raise(ERR_CODE_ILLEGAL_PARAMS, "can't unpack request"); - size_t bsize = 0; - for (int i = 0; i < txn->old_tuple->cardinality; i++) + size_t bsize = 0; + for (int i = 0; i < txn->old_tuple->cardinality; i++) bsize += fields[i]->len + varint32_sizeof(fields[i]->len); - txn->tuple = tuple_alloc(bsize); + txn->tuple = tuple_alloc(bsize); tuple_txn_ref(txn, txn->tuple); - txn->tuple->bsize = bsize; - txn->tuple->cardinality = txn->old_tuple->cardinality; + txn->tuple->cardinality = txn->old_tuple->cardinality; - uint8_t *p = txn->tuple->data; - for (int i = 0; i < txn->old_tuple->cardinality; i++) { + uint8_t *p = txn->tuple->data; + for (int i = 0; i < txn->old_tuple->cardinality; i++) { p = save_varint32(p, fields[i]->len); - memcpy(p, fields[i]->data, fields[i]->len); - p += fields[i]->len; - } + memcpy(p, fields[i]->data, fields[i]->len); + p += fields[i]->len; + } - run_hooks(txn, before_commit_update_hook); + validate_indeces(txn); + run_hooks(txn, before_commit_update_hook); - if(data->len != 0) + if (data->len != 0) box_raise(ERR_CODE_ILLEGAL_PARAMS, "can't unpack request"); - return -1; + return -1; } static void tuple_add_iov(struct box_txn *txn, struct box_tuple *tuple) { - tuple_txn_ref(txn, tuple); + tuple_txn_ref(txn, tuple); if (txn->old_format) { - add_iov_dup(&tuple->bsize, sizeof(uint16_t)); - add_iov_dup(&tuple->cardinality, sizeof(uint8_t)); - add_iov(tuple->data, tuple->bsize); - } else - add_iov(&tuple->bsize, + add_iov_dup(&tuple->bsize, sizeof(uint16_t)); + add_iov_dup(&tuple->cardinality, sizeof(uint8_t)); + add_iov(tuple->data, tuple->bsize); + } else + add_iov(&tuple->bsize, tuple->bsize + field_sizeof(struct box_tuple, bsize) + field_sizeof(struct box_tuple, cardinality)); } - static int __noinline__ process_select(struct box_txn *txn, u32 limit, u32 offset, struct tbuf *data, bool old_format) { - struct box_tuple *tuple; - uint32_t *found; + struct box_tuple *tuple; + uint32_t *found; u32 count = read_u32(data); found = palloc(fiber->pool, sizeof(*found)); add_iov(found, sizeof(*found)); *found = 0; - for (u32 i = 0; i < count; i++) { - if (!old_format) { + if (txn->index->type == TREE) { + for (u32 i = 0; i < count; i++) { u32 key_len = read_u32(data); - if (key_len != 1) - box_raise(ERR_CODE_ILLEGAL_PARAMS, "key must be single valued"); - } - void *key = read_field(data); - tuple = index_find(txn->index, 1, key); - if (tuple == NULL || tuple->flags & GHOST) - continue; + void *key = read_field(data); - if (offset > 0) { - offset--; - continue; + /* advance remaining fields of a key */ + for (int i = 1; i < key_len; i++) + read_field(data); + + struct tree_index_member *pattern = + alloc_search_pattern(txn->index, key_len, key); + txn->index->iterator_init(txn->index, pattern); + + while ((tuple = txn->index->iterator_next(txn->index, pattern)) != NULL) { + if (tuple->flags & GHOST) + continue; + + if (offset > 0) { + offset--; + continue; + } + + (*found)++; + tuple_add_iov(txn, tuple); + + if (--limit == 0) + break; + } + if (limit == 0) + break; } + } else { + for (u32 i = 0; i < count; i++) { + if (!old_format) { + u32 key_len = read_u32(data); + if (key_len != 1) + box_raise(ERR_CODE_ILLEGAL_PARAMS, + "key must be single valued"); + } + void *key = read_field(data); + tuple = txn->index->find(txn->index, key); + if (tuple == NULL || tuple->flags & GHOST) + continue; - (*found)++; - tuple_add_iov(txn, tuple); + if (offset > 0) { + offset--; + continue; + } - if (--limit == 0) - break; - } + (*found)++; + tuple_add_iov(txn, tuple); - if(data->len != 0) + if (--limit == 0) + break; + } + } + + if (data->len != 0) box_raise(ERR_CODE_ILLEGAL_PARAMS, "can't unpack request"); - return ERR_CODE_OK; + return ERR_CODE_OK; } - static int __noinline__ prepare_delete(struct box_txn *txn, void *key) { - txn->old_tuple = index_find(txn->index, 1, key); + txn->old_tuple = txn->index->find(txn->index, key); - if(txn->old_tuple == NULL) { - if (!txn->in_recover) { - u32 tuples_affected = 0; - add_iov_dup(&tuples_affected, sizeof(tuples_affected)); - } - return ERR_CODE_OK; - } else { + if (txn->old_tuple == NULL) { + if (!txn->in_recover) { + u32 tuples_affected = 0; + add_iov_dup(&tuples_affected, sizeof(tuples_affected)); + } + return ERR_CODE_OK; + } else { tuple_txn_ref(txn, txn->old_tuple); } - lock_tuple(txn, txn->old_tuple); - return -1; + lock_tuple(txn, txn->old_tuple); + return -1; } - static void commit_delete(struct box_txn *txn) { - if (!(txn->flags & BOX_QUIET) && !txn->in_recover) { - int tuples_affected = 1; - add_iov_dup(&tuples_affected, sizeof(tuples_affected)); - } + if (!(txn->flags & BOX_QUIET) && !txn->in_recover) { + int tuples_affected = 1; + add_iov_dup(&tuples_affected, sizeof(tuples_affected)); + } foreach_index(txn->n, index) - index_remove(index, txn->old_tuple); - tuple_ref(txn->old_tuple, -1); + index->remove(index, txn->old_tuple); + tuple_ref(txn->old_tuple, -1); - return; + return; } struct box_txn * txn_alloc(u32 flags) { - struct box_txn *txn = palloc(fiber->pool, sizeof(*txn)); - memset(txn, 0, sizeof(*txn)); + struct box_txn *txn = p0alloc(fiber->pool, sizeof(*txn)); txn->ref_tuples = tbuf_alloc(fiber->pool); - txn->flags |= flags; /* note - select will overwrite this flags */ + txn->flags |= flags; /* note - select will overwrite this flags */ return txn; } - void txn_cleanup(struct box_txn *txn) { @@ -746,48 +1091,47 @@ txn_cleanup(struct box_txn *txn) static void txn_commit(struct box_txn *txn) { - if (txn->op == 0) - return; + if (txn->op == 0) + return; - say_debug("box_commit(op:%s)", messages_strs[txn->op]); + say_debug("box_commit(op:%s)", messages_strs[txn->op]); unlock_tuples(txn); - if (txn->op == DELETE) - commit_delete(txn); - else - commit_replace(txn); + if (txn->op == DELETE) + commit_delete(txn); + else + commit_replace(txn); } static void txn_abort(struct box_txn *txn) { - if (txn->op == 0) + if (txn->op == 0) return; - say_debug("box_rollback(op:%s)", messages_strs[txn->op]); + say_debug("box_rollback(op:%s)", messages_strs[txn->op]); unlock_tuples(txn); if (txn->op == DELETE) - return; + return; - if (txn->op == INSERT) - rollback_replace(txn); + if (txn->op == INSERT) + rollback_replace(txn); } static bool op_is_select(u32 op) { - return op == SELECT || op == SELECT_LIMIT; + return op == SELECT || op == SELECT_LIMIT; } u32 -box_dispach(struct box_txn *txn, enum box_mode mode, u32 op, struct tbuf *data) +box_dispach(struct box_txn *txn, enum box_mode mode, u16 op, struct tbuf *data) { - u32 cardinality; - int ret_code; - void *data__data = data->data; - u32 data__len = data->len; + u32 cardinality; + int ret_code; + struct tbuf req = { .data = data->data, .len = data->len }; int saved_iov_cnt = fiber->iov_cnt; ev_tstamp start = ev_now(), stop; @@ -797,10 +1141,10 @@ box_dispach(struct box_txn *txn, enum box_mode mode, u32 op, struct tbuf *data) say_debug("box_dispach(%i)", op); if (!txn->in_recover) { - if (!op_is_select(op) && (mode == RO || !box_updates_allowed)) { - say_error("can't process %i command on RO port", op); - return ERR_CODE_NONMASTER; - } + if (!op_is_select(op) && (mode == RO || !box_updates_allowed)) { + say_error("can't process %i command on RO port", op); + return ERR_CODE_NONMASTER; + } fiber_register_cleanup((void *)txn_cleanup, txn); } @@ -809,7 +1153,7 @@ box_dispach(struct box_txn *txn, enum box_mode mode, u32 op, struct tbuf *data) txn->n = read_u32(data); txn->index = &namespace[txn->n].index[0]; - if(!namespace[txn->n].enabled) { + if (!namespace[txn->n].enabled) { say_warn("namespace %i is not enabled", txn->n); box_raise(ERR_CODE_ILLEGAL_PARAMS, "namespace is not enabled"); } @@ -819,17 +1163,17 @@ box_dispach(struct box_txn *txn, enum box_mode mode, u32 op, struct tbuf *data) void *key; u32 key_len; - switch (op) { - case INSERT: - txn->flags = read_u32(data); + switch (op) { + case INSERT: + txn->flags = read_u32(data); cardinality = read_u32(data); - if (namespace[txn->n].cardinality > 0 && namespace[txn->n].cardinality != cardinality) - box_raise(ERR_CODE_ILLEGAL_PARAMS, "tuple cardinality must match namespace cardinality"); - if (!txn->in_recover && txn->n == 3 && cardinality != 14) - box_raise(ERR_CODE_ILLEGAL_PARAMS, "tuple cardinality must match namespace cardinality"); - ret_code = prepare_replace(txn, cardinality, data); - stat_collect(messages_strs[op], 1); - break; + if (namespace[txn->n].cardinality > 0 + && namespace[txn->n].cardinality != cardinality) + box_raise(ERR_CODE_ILLEGAL_PARAMS, + "tuple cardinality must match namespace cardinality"); + ret_code = prepare_replace(txn, cardinality, data); + stat_collect(stat_base, op, 1); + break; case DELETE: key_len = read_u32(data); @@ -837,49 +1181,52 @@ box_dispach(struct box_txn *txn, enum box_mode mode, u32 op, struct tbuf *data) box_raise(ERR_CODE_ILLEGAL_PARAMS, "key must be single valued"); key = read_field(data); - if(data->len != 0) + if (data->len != 0) box_raise(ERR_CODE_ILLEGAL_PARAMS, "can't unpack request"); ret_code = prepare_delete(txn, key); - stat_collect(messages_strs[op], 1); - break; - - case SELECT: { - u32 i = read_u32(data); - u32 offset = read_u32(data); - u32 limit = read_u32(data); - - if (i > MAX_IDX) - box_raise(ERR_CODE_ILLEGAL_PARAMS, "index too big"); - txn->index = &namespace[txn->n].index[i]; - if (txn->index->key_position < 0) - box_raise(ERR_CODE_ILLEGAL_PARAMS, "index is invalid"); - - stat_collect(messages_strs[op], 1); - return process_select(txn, limit, offset, data, false); - } + stat_collect(stat_base, op, 1); + break; + + case SELECT:{ + u32 i = read_u32(data); + u32 offset = read_u32(data); + u32 limit = read_u32(data); + + if (i > MAX_IDX) + box_raise(ERR_CODE_ILLEGAL_PARAMS, "index too big"); + txn->index = &namespace[txn->n].index[i]; + if (txn->index->key_cardinality == 0) + box_raise(ERR_CODE_ILLEGAL_PARAMS, "index is invalid"); - case UPDATE_FIELDS: + stat_collect(stat_base, op, 1); + return process_select(txn, limit, offset, data, false); + } + + case UPDATE_FIELDS: txn->flags = read_u32(data); - stat_collect(messages_strs[op], 1); - ret_code = prepare_update_fields(txn, data, false); - break; + stat_collect(stat_base, op, 1); + ret_code = prepare_update_fields(txn, data, false); + break; default: - say_error("silverbox_dispach: unsupported command = %"PRIi32"", op); - return ERR_CODE_ILLEGAL_PARAMS; - } + say_error("silverbox_dispach: unsupported command = %" PRIi32 "", op); + return ERR_CODE_ILLEGAL_PARAMS; + } if (ret_code == -1) { - if (!txn->in_recover) { + if (!txn->in_recover) { + fiber_peer_name(fiber); /* fill the cookie */ struct tbuf *t = tbuf_alloc(fiber->pool); tbuf_append(t, &op, sizeof(op)); - tbuf_append(t, data__data, data__len); + tbuf_append(t, req.data, req.len); - if (!wal_write_v04(recovery_state, op, data__data, data__len)) { + i64 lsn = next_lsn(recovery_state, 0); + if (!wal_write(recovery_state, wal_tag, fiber->cookie, lsn, t)) { ret_code = ERR_CODE_UNKNOWN_ERROR; goto abort; } + confirm_lsn(recovery_state, lsn); } txn_commit(txn); @@ -887,92 +1234,108 @@ box_dispach(struct box_txn *txn, enum box_mode mode, u32 op, struct tbuf *data) if (stop - start > cfg.too_long_threshold) say_warn("too long %s: %.3f sec", messages_strs[op], stop - start); return 0; - } - + } return ret_code; -abort: + abort: fiber->iov_cnt = saved_iov_cnt; txn_abort(txn); return ret_code; } - static int box_xlog_sprint(struct tbuf *buf, const struct tbuf *t) { - struct row_v04 *row = row_v04(t); + struct row_v11 *row = row_v11(t); struct tbuf *b = palloc(fiber->pool, sizeof(*b)); b->data = row->data; b->len = row->len; - u32 op = row->type; + u16 tag, op; + u64 cookie; + struct sockaddr_in *peer = (void *)&cookie; + + u32 n, key_len; + void *key; + u32 cardinality, field_no; + u32 flags; + u32 op_cnt; - u32 n, key_len; - void *key; - u32 cardinality, field_no; - u32 flags; - u32 op_cnt; + tbuf_printf(buf, "lsn:%" PRIi64 " ", row->lsn); - tbuf_printf(buf, "lsn:%"PRIi64" ", row->lsn); + say_debug("b->len:%" PRIu32, b->len); - say_debug("b->len:%"PRIu32, b->len); - n = read_u32(b); + tag = read_u16(b); + cookie = read_u64(b); + op = read_u16(b); + n = read_u32(b); - tbuf_printf(buf, "%s ", messages_strs[op]); - tbuf_printf(buf, "n:%i ", n); + tbuf_printf(buf, "tm:%.3f t:%"PRIu16 " %s:%d %s n:%i", + row->tm, tag, inet_ntoa(peer->sin_addr), ntohs(peer->sin_port), + messages_strs[op], n); - switch (op) { - case INSERT: - flags = read_u32(b); + switch (op) { + case INSERT: + flags = read_u32(b); cardinality = read_u32(b); - if (b->len != valid_tuple(b, cardinality)) abort(); - tuple_print(buf, cardinality, b->data); - break; + if (b->len != valid_tuple(b, cardinality)) + abort(); + tuple_print(buf, cardinality, b->data); + break; case DELETE: key_len = read_u32(b); key = read_field(b); - if (b->len != 0) abort(); + if (b->len != 0) + abort(); tuple_print(buf, key_len, key); - break; + break; - case UPDATE_FIELDS: + case UPDATE_FIELDS: flags = read_u32(b); key_len = read_u32(b); - key = read_field(b); + key = read_field(b); op_cnt = read_u32(b); tbuf_printf(buf, "flags:%08X ", flags); - tuple_print(buf, key_len, key); + tuple_print(buf, key_len, key); while (op_cnt-- > 0) { field_no = read_u32(b); u8 op = read_u8(b); void *arg = read_field(b); - tbuf_printf(buf, " [field_no:%i op:", field_no); - switch(op) { - case 0: tbuf_printf(buf, "set "); break; - case 1: tbuf_printf(buf, "add "); break; - case 2: tbuf_printf(buf, "and "); break; - case 3: tbuf_printf(buf, "xor "); break; - case 4: tbuf_printf(buf, "or "); break; + tbuf_printf(buf, " [field_no:%i op:", field_no); + switch (op) { + case 0: + tbuf_printf(buf, "set "); + break; + case 1: + tbuf_printf(buf, "add "); + break; + case 2: + tbuf_printf(buf, "and "); + break; + case 3: + tbuf_printf(buf, "xor "); + break; + case 4: + tbuf_printf(buf, "or "); + break; } - tuple_print(buf, 1, arg); + tuple_print(buf, 1, arg); tbuf_printf(buf, "] "); } break; - default: - tbuf_printf(buf, "unknown wal op %" PRIi32, op); - } - return 0; + default: + tbuf_printf(buf, "unknown wal op %" PRIi32, op); + } + return 0; } - struct tbuf * -box_snap_reader(FILE * f, struct palloc_pool *pool) +box_snap_reader(FILE *f, struct palloc_pool *pool) { struct tbuf *row = tbuf_alloc(pool); const int header_size = sizeof(*box_snap_row(row)); @@ -985,76 +1348,102 @@ box_snap_reader(FILE * f, struct palloc_pool *pool) if (fread(box_snap_row(row)->data, box_snap_row(row)->data_size, 1, f) != 1) return NULL; - return row; + return convert_to_v11(row, snap_tag, default_cookie, 0); } - static int -snap_apply(struct recovery_state *r __unused__, const struct tbuf *t) +snap_apply(struct box_txn *txn, struct tbuf *t) { - struct box_snap_row *row = box_snap_row(t); - struct box_txn *txn = txn_alloc(0); - txn->in_recover = true; - txn->n = row->namespace; + struct box_snap_row *row; - if (txn->n == 25) - return 0; + read_u64(t); /* drop cookie */ + + row = box_snap_row(t); + txn->n = row->namespace; if (!namespace[txn->n].enabled) { say_error("namespace %i is not configured", txn->n); return -1; } txn->index = &namespace[txn->n].index[0]; - assert(txn->index->key_position >= 0); + assert(txn->index->key_cardinality > 0); struct tbuf *b = palloc(fiber->pool, sizeof(*b)); b->data = row->data; b->len = row->data_size; - if (prepare_replace(txn, row->tuple_size, b) != -1) { - say_error("unable prepare"); - return -1; - } + if (prepare_replace(txn, row->tuple_size, b) != -1) { + say_error("unable prepare"); + return -1; + } - txn->op = INSERT; + txn->op = INSERT; txn_commit(txn); + return 0; +} + +static int +wal_apply(struct box_txn *txn, struct tbuf *t) +{ + read_u64(t); /* drop cookie */ + + u16 type = read_u16(t); + if (box_dispach(txn, RW, type, t) != 0) + return -1; + txn_cleanup(txn); return 0; } static int -xlog_apply(struct recovery_state *r __unused__, const struct tbuf *t) +recover_row(struct recovery_state *r __unused__, struct tbuf *t) { - struct row_v04 *row = row_v04(t); - struct box_txn *txn = txn_alloc(0); + struct box_txn *txn = txn_alloc(0); + int result = -1; txn->in_recover = true; - assert(row->lsn > confirmed_lsn(r)); - - struct tbuf *b = palloc(fiber->pool, sizeof(*b)); - b->data = row->data; - b->len = row->len; + /* drop wal header */ + if (tbuf_peek(t, sizeof(struct row_v11)) == NULL) + return -1; - if (box_dispach(txn, RW, row->type, b) != 0) + u16 tag = read_u16(t); + if (tag == wal_tag) { + result = wal_apply(txn, t); + } else if (tag == snap_tag) { + result = snap_apply(txn, t); + } else { + say_error("unknown row tag: %i", (int)tag); return -1; + } txn_cleanup(txn); - return 0; + return result; } + static int -snap_print(struct recovery_state *r __unused__, const struct tbuf *t) +snap_print(struct recovery_state *r __unused__, struct tbuf *t) { struct tbuf *out = tbuf_alloc(t->pool); - struct box_snap_row *row = box_snap_row(t); + struct box_snap_row *row; + struct row_v11 *raw_row = row_v11(t); + + struct tbuf *b = palloc(fiber->pool, sizeof(*b)); + b->data = raw_row->data; + b->len = raw_row->len; + + (void)read_u16(b); /* drop tag */ + (void)read_u64(b); /* drop cookie */ - tuple_print(out, row->tuple_size , row->data); + row = box_snap_row(b); + + tuple_print(out, row->tuple_size, row->data); printf("n:%i %*s\n", row->namespace, (int)out->len, (char *)out->data); return 0; } static int -xlog_print(struct recovery_state *r __unused__, const struct tbuf *t) +xlog_print(struct recovery_state *r __unused__, struct tbuf *t) { struct tbuf *out = tbuf_alloc(t->pool); int res = box_xlog_sprint(out, t); @@ -1063,7 +1452,6 @@ xlog_print(struct recovery_state *r __unused__, const struct tbuf *t) return res; } - static void custom_init(void) { @@ -1084,45 +1472,145 @@ custom_init(void) namespace[i].cardinality = cfg.namespace[i]->cardinality; int estimated_rows = cfg.namespace[i]->estimated_rows; + if (cfg.namespace[i]->index == NULL) + panic("(namespace = %" PRIu32 ") at least one index must be defined", i); + for (int j = 0; j < nelem(namespace[i].index); j++) { + struct index *index = &namespace[i].index[j]; + u32 max_key_fieldno = 0; + if (cfg.namespace[i]->index[j] == NULL) break; - namespace[i].index[j].key_position = cfg.namespace[i]->index[j]->key_position; - if (namespace[i].index[j].key_position == -1) - continue; + if (cfg.namespace[i]->index[j]->key_field == NULL) + panic("(namespace = %" PRIu32 " index = %" PRIu32 ") " + "at least one field must be defined", i, j); + for (int k = 0; cfg.namespace[i]->index[j]->key_field[k] != NULL; k++) { + if (cfg.namespace[i]->index[j]->key_field[k]->fieldno == -1) + break; - if (strcmp(cfg.namespace[i]->index[j]->type, "NUM") == 0) { - namespace[i].index[j].find = index_find_hash_num; - namespace[i].index[j].remove = index_remove_hash_num; - namespace[i].index[j].replace = index_replace_hash_num; - namespace[i].index[j].namespace = &namespace[i]; - namespace[i].index[j].type = INDEX_NUM; - namespace[i].index[j].map.int_map = kh_init(int2ptr_map, NULL); - if (estimated_rows > 0) - kh_resize(int2ptr_map, namespace[i].index[j].map.int_map, estimated_rows); - } else if (strcmp(cfg.namespace[i]->index[j]->type, "STR") == 0) { - namespace[i].index[j].find = index_find_hash_str; - namespace[i].index[j].remove = index_remove_hash_str; - namespace[i].index[j].replace = index_replace_hash_str; - namespace[i].index[j].namespace = &namespace[i]; - namespace[i].index[j].type = INDEX_STR; - namespace[i].index[j].map.str_map = kh_init(lstr2ptr_map, NULL); - if (estimated_rows > 0) - kh_resize(lstr2ptr_map, namespace[i].index[j].map.str_map, estimated_rows); - } else { - say_warn("unknown index type `%s'", cfg.namespace[i]->index[j]->type); + max_key_fieldno = + MAX(max_key_fieldno, + cfg.namespace[i]->index[j]->key_field[k]->fieldno); + + ++index->key_cardinality; + } + + if (index->key_cardinality == 0) continue; + + index->key_field = salloc(sizeof(index->key_field[0]) * + index->key_cardinality); + if (index->key_field == NULL) + panic("can't allocate key_field for index"); + + index->field_cmp_order_cnt = max_key_fieldno + 1; + index->field_cmp_order = + salloc(sizeof(index->field_cmp_order[0]) * + index->field_cmp_order_cnt); + if (index->field_cmp_order == NULL) + panic("can't allocate field_cmp_order for index"); + memset(index->field_cmp_order, -1, + sizeof(index->field_cmp_order[0]) * index->field_cmp_order_cnt); + + for (int k = 0; cfg.namespace[i]->index[j]->key_field[k] != NULL; k++) { + if (cfg.namespace[i]->index[j]->key_field[k]->fieldno == -1) + break; + + index->key_field[k].fieldno = + cfg.namespace[i]->index[j]->key_field[k]->fieldno; + if (strcmp(cfg.namespace[i]->index[j]->key_field[k]->type, "NUM") == + 0) + index->key_field[k].type = NUM; + else if (strcmp(cfg.namespace[i]->index[j]->key_field[k]->type, + "STR") == 0) + index->key_field[k].type = STR; + else + panic("(namespace = %" PRIu32 " index = %" PRIu32 ") " + "unknown field data type: `%s'", + i, j, cfg.namespace[i]->index[j]->key_field[k]->type); + + index->field_cmp_order[index->key_field[k].fieldno] = k; } + + index->search_pattern = palloc(eter_pool, SIZEOF_TREE_INDEX_MEMBER(index)); + + if (cfg.namespace[i]->index[j]->unique == 0) + index->unique = false; + else if (cfg.namespace[i]->index[j]->unique == 1) + index->unique = true; + else + panic("(namespace = %" PRIu32 " index = %" PRIu32 ") " + "unique property is undefined", i, j); + + if (strcmp(cfg.namespace[i]->index[j]->type, "HASH") == 0) { + if (index->key_cardinality != 1) + panic("(namespace = %" PRIu32 " index = %" PRIu32 ") " + "hash index must have single-filed key", i, j); + + index->enabled = true; + index->type = HASH; + + if (index->unique == false) + panic("(namespace = %" PRIu32 " index = %" PRIu32 ") " + "hash index must be unique", i, j); + + if (index->key_field->type == NUM) { + index->find = index_find_hash_num; + index->find_by_tuple = index_find_hash_by_tuple; + index->remove = index_remove_hash_num; + index->replace = index_replace_hash_num; + index->namespace = &namespace[i]; + index->idx.int_hash = kh_init(int2ptr_map, NULL); + + if (estimated_rows > 0) + kh_resize(int2ptr_map, index->idx.int_hash, + estimated_rows); + } else { + index->find = index_find_hash_str; + index->find_by_tuple = index_find_hash_by_tuple; + index->remove = index_remove_hash_str; + index->replace = index_replace_hash_str; + index->namespace = &namespace[i]; + index->idx.str_hash = kh_init(lstr2ptr_map, NULL); + + if (estimated_rows > 0) + kh_resize(lstr2ptr_map, index->idx.str_hash, + estimated_rows); + } + } else if (strcmp(cfg.namespace[i]->index[j]->type, "TREE") == 0) { + index->enabled = false; + index->type = TREE; + + index->find = index_find_tree; + index->find_by_tuple = index_find_tree_by_tuple; + index->remove = index_remove_tree_str; + index->replace = index_replace_tree_str; + index->iterator_init = index_iterator_init_tree_str; + index->iterator_next = index_iterator_next_tree_str; + index->namespace = &namespace[i]; + + index->idx.tree = palloc(eter_pool, sizeof(*index->idx.tree)); + } else + panic("namespace = %" PRIu32 " index = %" PRIu32 ") " + "unknown index type `%s'", + i, j, cfg.namespace[i]->index[j]->type); } + + if (namespace[i].index[0].key_cardinality == 0) + panic("(namespace = %" PRIu32 ") namespace must have at least one index", + i); + if (namespace[i].index[0].type != HASH) + panic("(namespace = %" PRIu32 ") namespace first index must be HASH", i); + namespace[i].enabled = true; namespace[i].n = i; + say_info("namespace %i successfully configured", i); } } - static u32 box_process_ro(u32 op, struct tbuf *request_data) { @@ -1147,8 +1635,7 @@ title(const char *fmt, ...) if (cfg.memcached) set_proc_title("memcached:%s%s pri:%i adm:%i", - buf, custom_proc_title, - cfg.primary_port, cfg.admin_port); + buf, custom_proc_title, cfg.primary_port, cfg.admin_port); else set_proc_title("box:%s%s pri:%i sec:%i adm:%i", buf, custom_proc_title, @@ -1163,8 +1650,10 @@ box_bound_to_primary(void *data __unused__) if (cfg.remote_hot_standby) { say_info("starting remote hot standby"); status = palloc(eter_pool, 64); - snprintf(status, 64, "hot_standby/%s:%i%s", cfg.wal_feeder_ipaddr, cfg.wal_feeder_port, custom_proc_title); - recover_follow_remote(recovery_state, cfg.wal_feeder_ipaddr, cfg.wal_feeder_port); + snprintf(status, 64, "hot_standby/%s:%i%s", cfg.wal_feeder_ipaddr, + cfg.wal_feeder_port, custom_proc_title); + recover_follow_remote(recovery_state, cfg.wal_feeder_ipaddr, cfg.wal_feeder_port, + default_remote_row_handler); title("hot_standby/%s:%i", cfg.wal_feeder_ipaddr, cfg.wal_feeder_port); } else { @@ -1180,22 +1669,103 @@ memcached_bound_to_primary(void *data __unused__) { box_bound_to_primary(NULL); - if (!cfg.remote_hot_standby) { - struct fiber *expire = fiber_create("memecached_expire", -1, -1, memcached_expire, NULL); + if (0 && !cfg.remote_hot_standby) { + struct fiber *expire = + fiber_create("memecached_expire", -1, -1, memcached_expire, NULL); if (expire == NULL) panic("can't stared expire fiber"); fiber_call(expire); } } +static void +build_indexes(void) +{ + for (u32 n = 0; n < nelem(namespace); ++n) { + u32 n_tuples, estimated_tuples; + struct tree_index_member *members[nelem(namespace[n].index)] = { NULL }; + + if (namespace[n].enabled == false) + continue; + + n_tuples = kh_size(namespace[n].index[0].idx.hash); + estimated_tuples = n_tuples * 1.2; + + say_info("build_indexes: n = %" PRIu32 ": build arrays", n); + + khiter_t k; + u32 i = 0; + assoc_foreach(namespace[n].index[0].idx.hash, k) { + for (u32 idx = 0;; idx++) { + struct index *index = &namespace[n].index[idx]; + struct tree_index_member *member; + struct tree_index_member *m; + + if (index->key_cardinality == 0) + break; + + if (index->type != TREE) + continue; + + member = members[idx]; + if (member == NULL) { + member = malloc(estimated_tuples * + SIZEOF_TREE_INDEX_MEMBER(index)); + if (member == NULL) + panic("build_indexes: malloc failed: %m"); + + members[idx] = member; + } + + m = (struct tree_index_member *) + ((char *)member + i * SIZEOF_TREE_INDEX_MEMBER(index)); + + tuple2tree_index_member(index, + kh_value(namespace[n].index[0].idx.hash, + k), + &m); + } + + ++i; + } + + say_info("build_indexes: n = %" PRIu32 ": build trees", n); + + for (u32 idx = 0;; idx++) { + struct index *index = &namespace[n].index[idx]; + struct tree_index_member *member = members[idx]; + + if (index->key_cardinality == 0) + break; + + if (index->type != TREE) + continue; + + assert(index->enabled == false); + + say_info("build_indexes: n = %" PRIu32 " idx = %" PRIu32 ": build tree", n, + idx); + + /* if n_tuples == 0 then estimated_tuples = 0, member == NULL, tree is empty */ + sptree_str_t_init(index->idx.tree, + SIZEOF_TREE_INDEX_MEMBER(index), + member, n_tuples, estimated_tuples, + (void *)tree_index_member_compare, index); + index->enabled = true; + + say_info("build_indexes: n = %" PRIu32 " idx = %" PRIu32 ": end", n, idx); + } + } +} void mod_init(void) { + stat_base = stat_register(messages_strs, messages_MAX); for (int i = 0; i < nelem(namespace); i++) { namespace[i].enabled = false; for (int j = 0; j < MAX_IDX; j++) - namespace[i].index[j].key_position = -1; + namespace[i].index[j].key_cardinality = 0; } if (cfg.custom_proc_title == NULL) @@ -1219,30 +1789,55 @@ mod_init(void) } recovery_state = recover_init(cfg.snap_dir, cfg.wal_dir, - box_snap_reader, snap_apply, xlog_apply, - cfg.rows_per_wal, cfg.wal_fsync_delay, cfg.snap_io_rate_limit, - cfg.wal_writer_inbox_size, init_storage ? RECOVER_READONLY : 0, NULL); + box_snap_reader, recover_row, + cfg.rows_per_wal, cfg.wal_fsync_delay, + cfg.wal_writer_inbox_size, + init_storage ? RECOVER_READONLY : 0, NULL); + + recovery_state->snap_io_rate_limit = cfg.snap_io_rate_limit * 1024 * 1024; + recovery_setup_panic(recovery_state, cfg.panic_on_snap_error, cfg.panic_on_wal_error); /* initialize hashes _after_ starting wal writer */ if (cfg.memcached != 0) { int n = cfg.memcached_namespace > 0 ? cfg.memcached_namespace : MEMCACHED_NAMESPACE; - namespace[n].enabled = true; - namespace[n].index[0].map.str_map = kh_init(lstr2ptr_map, NULL); - namespace[n].index[0].find = index_find_hash_str; - namespace[n].index[0].remove = index_remove_hash_str; - namespace[n].index[0].replace = index_replace_hash_str; + + cfg.namespace = palloc(eter_pool, (n + 1) * sizeof(cfg.namespace[0])); + for (u32 i = 0; i <= n; ++i) { + cfg.namespace[i] = palloc(eter_pool, sizeof(cfg.namespace[0][0])); + cfg.namespace[i]->enabled = false; + } + + cfg.namespace[n]->enabled = true; + cfg.namespace[n]->cardinality = 4; + cfg.namespace[n]->estimated_rows = 0; + cfg.namespace[n]->index = palloc(eter_pool, 2 * sizeof(cfg.namespace[n]->index[0])); + cfg.namespace[n]->index[0] = + palloc(eter_pool, sizeof(cfg.namespace[n]->index[0][0])); + cfg.namespace[n]->index[1] = NULL; + cfg.namespace[n]->index[0]->type = "HASH"; + cfg.namespace[n]->index[0]->unique = 1; + cfg.namespace[n]->index[0]->key_field = + palloc(eter_pool, 2 * sizeof(cfg.namespace[n]->index[0]->key_field[0])); + cfg.namespace[n]->index[0]->key_field[0] = + palloc(eter_pool, sizeof(cfg.namespace[n]->index[0]->key_field[0][0])); + cfg.namespace[n]->index[0]->key_field[1] = NULL; + cfg.namespace[n]->index[0]->key_field[0]->fieldno = 0; + cfg.namespace[n]->index[0]->key_field[0]->type = "STR"; + memcached_index = &namespace[n].index[0]; - memcached_index->key_position = 0; - memcached_index->type = INDEX_STR; - } else { - custom_init(); } + custom_init(); + if (init_storage) return; recover(recovery_state, 0); + title("build_indexes"); + + build_indexes(); + title("orphan"); if (cfg.local_hot_standby) { @@ -1253,13 +1848,16 @@ mod_init(void) } if (cfg.memcached != 0) { - fiber_server(tcp_server, cfg.primary_port, memcached_handler, NULL, memcached_bound_to_primary); + fiber_server(tcp_server, cfg.primary_port, memcached_handler, NULL, + memcached_bound_to_primary); } else { if (cfg.secondary_port != 0) - fiber_server(tcp_server, cfg.secondary_port, iproto_interact, box_process_ro, NULL); + fiber_server(tcp_server, cfg.secondary_port, iproto_interact, + box_process_ro, NULL); if (cfg.primary_port != 0) - fiber_server(tcp_server, cfg.primary_port, iproto_interact, box_process, box_bound_to_primary); + fiber_server(tcp_server, cfg.primary_port, iproto_interact, box_process, + box_bound_to_primary); } say_info("initialized"); @@ -1274,32 +1872,32 @@ mod_cat(const char *filename) void mod_snapshot(struct log_io_iter *i) { - struct tbuf *row = tbuf_alloc(fiber->pool); + struct tbuf *row; struct box_snap_row header; struct box_tuple *tuple; - khiter_t k; + khiter_t k; - for(uint32_t n = 0; n < nelem(namespace); ++n) { - if (!namespace[n].enabled) + for (uint32_t n = 0; n < nelem(namespace); ++n) { + if (!namespace[n].enabled) continue; - assoc_foreach(namespace[n].index[0].map.int_map, k) { - tuple = kh_value(namespace[n].index[0].map.int_map, k); + assoc_foreach(namespace[n].index[0].idx.int_hash, k) { + tuple = kh_value(namespace[n].index[0].idx.int_hash, k); - if (tuple->flags & GHOST) // do not save fictive rows + if (tuple->flags & GHOST) // do not save fictive rows continue; header.namespace = n; header.tuple_size = tuple->cardinality; header.data_size = tuple->bsize; - tbuf_reset(row); + row = tbuf_alloc(fiber->pool); tbuf_append(row, &header, sizeof(header)); tbuf_append(row, tuple->data, tuple->bsize); - snapshot_write_row(i, row); + snapshot_write_row(i, snap_tag, default_cookie, row); } - } + } } void @@ -1309,7 +1907,15 @@ mod_info(struct tbuf *out) tbuf_printf(out, " version: \"%s\"\r\n", tarantool_version()); tbuf_printf(out, " uptime: %i\r\n", (int)tarantool_uptime()); tbuf_printf(out, " pid: %i\r\n", getpid()); - tbuf_printf(out, " wal_writer_pid: %"PRIi64"\r\n", (i64)wal_writer(recovery_state)->pid); - tbuf_printf(out, " lsn: %"PRIi64"\r\n", confirmed_lsn(recovery_state)); + tbuf_printf(out, " wal_writer_pid: %" PRIi64 "\r\n", (i64)recovery_state->wal_writer->pid); + tbuf_printf(out, " lsn: %" PRIi64 "\r\n", recovery_state->confirmed_lsn); + tbuf_printf(out, " recovery_lag: %.3f\r\n", recovery_state->recovery_lag); + tbuf_printf(out, " recovery_last_update: %.3f\r\n", recovery_state->recovery_last_update_tstamp); tbuf_printf(out, " status: %s\r\n", status); } + +void +mod_exec(char *str __unused__, int len __unused__, struct tbuf *out) +{ + tbuf_printf(out, "unimplemented\r\n"); +} diff --git a/mod/silverbox/box.h b/mod/silverbox/box.h index ccc7fc188488de34c7251e996679f3b7977673c4..2c3a51ce4ca4dc9416cab649ee96c809b567fb48 100644 --- a/mod/silverbox/box.h +++ b/mod/silverbox/box.h @@ -33,18 +33,70 @@ extern bool box_updates_allowed; void memcached_handler(void *_data __unused__); struct namespace; +struct box_tuple; + +struct field { + u32 len; + union { + u32 u32; + + u8 data[sizeof(void *)]; + + void *data_ptr; + }; +}; + +enum field_data_type { NUM, STR }; + +struct tree_index_member { + struct box_tuple *tuple; + struct field key[]; +}; + +#define SIZEOF_TREE_INDEX_MEMBER(index) \ + (sizeof(struct tree_index_member) + sizeof(struct field) * (index)->key_cardinality) + +#include <third_party/sptree.h> +SPTREE_DEF(str_t, realloc); + +// #include <mod/silverbox/tree.h> struct index { - struct box_tuple *(*find)(struct index *index, int key_len, void *key); - void (*remove)(struct index *index, void *key); - void (*replace)(struct index *index, void *key, void *value); + bool enabled; + + bool unique; + + struct box_tuple *(*find) (struct index * index, void *key); /* only for unique lookups */ + struct box_tuple *(*find_by_tuple) (struct index * index, struct box_tuple * pattern); + void (*remove) (struct index * index, struct box_tuple *); + void (*replace) (struct index * index, struct box_tuple *, struct box_tuple *); + void (*iterator_init) (struct index *, struct tree_index_member * pattern); + struct box_tuple *(*iterator_next) (struct index *, struct tree_index_member * pattern); union { - khash_t(lstr2ptr_map) *str_map; - khash_t(int2ptr_map) *int_map; - } map; + khash_t(lstr2ptr_map) * str_hash; + khash_t(int2ptr_map) * int_hash; + khash_t(int2ptr_map) * hash; + sptree_str_t *tree; + } idx; + void *iterator; + bool iterator_empty; + struct namespace *namespace; - int key_position; - enum { INDEX_NUM, INDEX_STR } type; + + struct { + struct { + u32 fieldno; + enum field_data_type type; + } *key_field; + u32 key_cardinality; + + u32 *field_cmp_order; + u32 field_cmp_order_cnt; + }; + + struct tree_index_member *search_pattern; + + enum { HASH, TREE } type; }; extern struct index *memcached_index; @@ -58,35 +110,34 @@ struct namespace { }; struct box_tuple { - u16 refs; - u16 flags; - u32 bsize; - u32 cardinality; - u8 data[0]; + u16 refs; + u16 flags; + u32 bsize; + u32 cardinality; + u8 data[0]; } __packed__; - struct box_txn { - int op; - u32 flags; + int op; + u32 flags; struct namespace *namespace; - struct index *index; - int n; + struct index *index; + int n; struct tbuf *ref_tuples; - struct box_tuple *old_tuple; - struct box_tuple *tuple; + struct box_tuple *old_tuple; + struct box_tuple *tuple; struct box_tuple *lock_tuple; bool in_recover, old_format; }; - enum tuple_flags { WAL_WAIT = 0x1, - GHOST = 0x2, - NEW = 0x4 + GHOST = 0x2, + NEW = 0x4, + SEARCH = 0x8 }; enum box_mode { @@ -95,9 +146,10 @@ enum box_mode { }; #define BOX_RETURN_TUPLE 1 +#define BOX_ADD 2 +#define BOX_REPLACE 4 #define BOX_QUIET 8 - /* deprecated commands: _(INSERT, 1) @@ -126,10 +178,10 @@ enum box_mode { ENUM(messages, MESSAGES); -struct box_tuple *index_find(struct index *index, int key_len, void *key); +struct box_tuple *index_find(struct index *index, void *key); struct box_txn *txn_alloc(u32 flags); -u32 box_dispach(struct box_txn *txn, enum box_mode mode, u32 op, struct tbuf *data); +u32 box_dispach(struct box_txn *txn, enum box_mode mode, u16 op, struct tbuf *data); void tuple_txn_ref(struct box_txn *txn, struct box_tuple *tuple); void txn_cleanup(struct box_txn *txn); diff --git a/mod/silverbox/box_cfg.cfg_tmpl b/mod/silverbox/box_cfg.cfg_tmpl index d9b6cc60ab95fc7e80d0d32f88e227ac9aeae9de..1bc8a9bf9d4a9b1a7d8b035dd898384057fa5551 100644 --- a/mod/silverbox/box_cfg.cfg_tmpl +++ b/mod/silverbox/box_cfg.cfg_tmpl @@ -28,6 +28,10 @@ memcached_expire_per_loop=1024 # tarantool will try iterate all rows within this time memcached_expire_full_sweep=3600 + +# do not write snapshot faster then snap_io_rate_limit MBytes/sec +snap_io_rate_limit=0.0 + # Write no more rows in WAL rows_per_wal=500000 @@ -44,6 +48,12 @@ local_hot_standby=0 # delay in fractional seconds between successive re-readings of wal_dir wal_dir_rescan_delay=0.1 + +# panic if where is error reading snap or wal +# be default panic any snapshot reading error and ignore errors then reading wals +panic_on_snap_error=1 +panic_on_wal_error=0 + # Remote hot standby (if enabled server will run in hot standby mode # continuously fetching WAL records from wal_feeder_ipaddr:wal_feeder_port remote_hot_standby=0 @@ -56,7 +66,11 @@ namespace = [ cardinality = -1 estimated_rows = 0 index = [ - type = "NUM" - key_position = -1 + type = "" + unique = -1 + key_field = [ + fieldno = -1 + type = "" + ] ] -] \ No newline at end of file +] diff --git a/mod/silverbox/client/perl/MANIFEST b/mod/silverbox/client/perl/MANIFEST new file mode 100755 index 0000000000000000000000000000000000000000..a6c5f9954c52a8f15db2066861100b238c79a9e3 --- /dev/null +++ b/mod/silverbox/client/perl/MANIFEST @@ -0,0 +1,5 @@ +Makefile.PL +MANIFEST +lib/MR/IProto.pm +lib/MR/SilverBox.pm +lib/MR/Storage/Const.pm diff --git a/mod/silverbox/client/perl/Makefile.PL b/mod/silverbox/client/perl/Makefile.PL new file mode 100755 index 0000000000000000000000000000000000000000..08d6dabb058446d6641780f0d15dba3c88ece0ae --- /dev/null +++ b/mod/silverbox/client/perl/Makefile.PL @@ -0,0 +1,16 @@ +use ExtUtils::MakeMaker; + +WriteMakefile( + NAME => "MR::SilverBox", + VERSION_FROM => "lib/MR/SilverBox.pm", + MAKEFILE => 'Makefile', + PREREQ_PM => { + 'Scalar::Util' => 0, + 'List::Util' => 0, + 'List::MoreUtils' => 0, + 'Time::HiRes' => 0, + 'String::CRC32' => 0, + 'Exporter' => 0, + 'Fcntl' => 0, + }, +); diff --git a/mod/silverbox/client/perl/MR/IProto.pm b/mod/silverbox/client/perl/lib/MR/IProto.pm similarity index 77% rename from mod/silverbox/client/perl/MR/IProto.pm rename to mod/silverbox/client/perl/lib/MR/IProto.pm index afc15b36d50aeeaf5484fc78409ca5016ad44ebe..f32ee14ac14824d4cc52e27b9e7599be19df10da 100644 --- a/mod/silverbox/client/perl/MR/IProto.pm +++ b/mod/silverbox/client/perl/lib/MR/IProto.pm @@ -1,21 +1,16 @@ package MR::IProto; -# $Id: IProto.pm,v 1.166 2010/04/20 15:02:49 nevinitsin Exp $ - use strict; -use Exporter; -use base qw/Exporter/; - -our @EXPORT_OK = qw/ipro/; - -use Socket qw(PF_INET SOCK_STREAM SOL_SOCKET SO_SNDTIMEO SO_RCVTIMEO TCP_NODELAY); -use IO::Handle (); -use Errno qw( EINPROGRESS EWOULDBLOCK EISCONN EAGAIN ); -use Carp qw(carp confess); -use vars qw($PROTO_TCP %sockets); +use Socket qw(PF_INET SOCK_STREAM SOL_SOCKET SO_SNDTIMEO SO_RCVTIMEO SO_KEEPALIVE TCP_NODELAY); use String::CRC32 qw(crc32); use Time::HiRes qw/time sleep/; +use Fcntl; + +use vars qw($VERSION $PROTO_TCP %sockets); +$VERSION = 0; + +use overload '""' => sub { "$_[0]->{name}\[$_[0]->{_last_server}]" }; BEGIN { if (eval {require 5.8.3}) { @@ -26,56 +21,49 @@ BEGIN { } } -sub ipro ($) { # $ stands for LPS, % stands for LPS with 2-byte len. - my $s = $_[0]; - $s =~ s{\$}{L/a*}g; - $s =~ s{\%}{S/a*}g; - $s =~ s{\s}{}gs; - return $s; -} - -*mPOP::RemoteStorTimeOut = sub { 0 } unless defined &mPOP::RemoteStorTimeOut; - $PROTO_TCP = 0; %sockets = (); -sub DEFAULT_RETRY_DELAY() { 0 } -sub DEFAULT_MAX_REQUEST_RETRIES() { 2 }; -sub DEFAULT_TIMEOUT () { 2 }; -sub RR () { 1 } -sub HASH () { 2 } -sub KETAMA () { 3 } +sub DEFAULT_RETRY_DELAY () { 0 } +sub DEFAULT_MAX_REQUEST_RETRIES () { 2 } +sub DEFAULT_TIMEOUT () { 2 } + +sub RR () { 1 } +sub HASH () { 2 } +sub KETAMA () { 3 } -sub DisconnectAll -{ +sub confess { die @_ }; + +sub DisconnectAll { close $_ foreach (values %sockets); %sockets = (); } -sub new -{ - my $class = shift; +sub new { + my ($class, $args) = @_; my $self = {}; bless $self, $class; - my ($args) = @_; - - $self->{debug} = defined($args->{debug}) ? $args->{debug} : mPOP::Config::GetValue('IProtoDebug'); - $self->{balance} = $args->{balance} && $args->{balance} eq 'hash-crc32' ? HASH() : RR(); - $self->{balance} = KETAMA() if ($args->{balance} && $args->{balance} eq 'ketama'); - $self->{rotateservers} = 1 unless $args->{norotateservers}; - $self->{max_request_retries} = $args->{max_request_retries} || DEFAULT_MAX_REQUEST_RETRIES(); - $self->{retry_delay} = $args->{retry_delay} || DEFAULT_RETRY_DELAY(); - $self->{dump_no_ints} = 1 if $args->{dump_no_ints}; - $self->{tcp_nodelay} = 1 if $args->{tcp_nodelay}; - $self->{param} = $args->{param}; - - my $servers = $args->{servers} || confess("MR::IProto->new: no servers given"); + $self->{debug} = $args->{debug} || 0; + $self->{balance} = $args->{balance} && $args->{balance} eq 'hash-crc32' ? HASH() : RR(); + $self->{balance} = KETAMA() if $args->{balance} && $args->{balance} eq 'ketama'; + $self->{rotateservers} = 1 unless $args->{norotateservers}; + $self->{max_request_retries} = $args->{max_request_retries} || DEFAULT_MAX_REQUEST_RETRIES(); + $self->{retry_delay} = $args->{retry_delay} || DEFAULT_RETRY_DELAY(); + $self->{dump_no_ints} = 1 if $args->{dump_no_ints}; + $self->{tcp_nodelay} = 1 if $args->{tcp_nodelay}; + $self->{tcp_keepalive} = $args->{tcp_keepalive} || 0; + $self->{param} = $args->{param}; + $self->{_last_server} = ''; + + $self->{name} = $args->{name} or ($self->{name}) = caller; + + my $servers = $args->{servers} || confess("${class}->new: no servers given"); _parse_servers $self 'servers', $servers; if ($self->{balance} == RR()) { $self->{aviable_servers} = [@{$self->{servers}}]; #make copy - $servers = $args->{broadcast_servers} || ''; # confess("MR::IProto->new: no broadcast servers given"); + $servers = $args->{broadcast_servers} || ''; # confess("${class}->new: no broadcast servers given"); _parse_servers $self 'broadcast_servers', $servers; } elsif ($self->{balance} == KETAMA()) { $self->{'ketama'} = []; @@ -91,7 +79,7 @@ sub new my $timeout = exists $args->{timeout} ? $args->{timeout} : DEFAULT_TIMEOUT(); SetTimeout $self $timeout; - confess "MR::Iproto: no servers given" unless @{$self->{servers}}; + confess "${class}: no servers given" unless @{$self->{servers}}; return $self; } @@ -99,8 +87,7 @@ sub GetParam { return $_[0]->{param}; } -sub Chat -{ +sub Chat { my ($self, %message) = @_; confess "wrong msg id" unless exists($message{msg}); @@ -111,7 +98,7 @@ sub Chat for (my $try = 1; $try <= $retries; $try++) { sleep $self->{retry_delay} if $try > 1 && $self->{retry_delay}; - $self->{debug} >= 1 && _debug $self "chat msg=$message{msg} $try of $retries total"; + $self->{debug} >= 2 && _debug $self "chat msg=$message{msg} $try of $retries total"; my $ret = $self->Chat1(%message); if ($ret->{ok}) { @@ -130,7 +117,7 @@ sub Chat1 { my $server = _select_server $self $message{key}; unless($server) { - $self->{_error} .= 'All servers blocked by remote_stor_pinger'; + $self->{_error} ||= 'Could not find a valid server'; return; } @@ -147,8 +134,7 @@ sub Chat1 { # private methods # order of method declaration is important, see perlobj -sub _parse_servers -{ +sub _parse_servers { my ($self, $key, $line) = @_; my @servers; my $weighted = $self->{balance} == HASH(); @@ -159,23 +145,12 @@ sub _parse_servers push @servers, { host => $host, port => $port, ok => 1, repr => "$host:$port" } for (1..$weight); } else { my ($host, $port) = $server =~ /(.+):(\d+)/; - push @servers, { host => $host, port => $port, repr => "$host:$port" } - + push @servers, { host => $host, port => $port, repr => "$host:$port" }; } } $self->{$key} = \@servers; } -sub _stats { - my ($start_time, $server) = @_; - return unless $start_time; - - my $repr = "$server->{host}:$server->{port}"; - $repr =~ s/\./_/g; - - $mPOP::Carbon::bulk{"wallclock_iproto.$repr"} += time() - $start_time; -} - sub _chat { my ($self, $server, $message) = @_; _a_init $self $server, $message @@ -203,7 +178,7 @@ sub _a_init { $self->{_start_time} = time; $self->{_connecting} = 1; $self->{sock} = $sockets{$server->{repr}} || _connect $self $server, \$self->{_error}, $async; - IO::Handle::blocking($self->{sock},!$async) if $self->{sock}; + _set_blocking($self->{sock},!$async) if $self->{sock}; return $self->{sock}; } @@ -213,12 +188,15 @@ sub _a_send { $self->{_connecting} = 0; $self->{_timedout} = 0; while(length $self->{_write_buf}) { + local $! = 0; my $res = syswrite($self->{sock}, $self->{_write_buf}); if(defined $res && $res == 0) { + $self->{debug} <= 0 || _debug $self "$!" and next if $!{EINTR}; $self->{_error} .= "Server unexpectedly closed connection (${\length $self->{_write_buf}} bytes unwritten)"; return; } if(!defined $res) { + $self->{debug} <= 0 || _debug $self "$!" and next if $!{EINTR}; $self->{_timedout} = !!$!{EAGAIN}; $self->{_error} .= $! unless $self->{_async} && $!{EAGAIN}; return; @@ -240,6 +218,7 @@ sub _a_recv { $self->{_connecting} = 0; $self->{_timedout} = 0; while(1) { + local $! = 0; my $res; while($self->{_to_read} and $res = sysread($self->{sock}, my $buffer, $self->{_to_read} ) ) { $self->{_to_read} -= $res; @@ -247,10 +226,12 @@ sub _a_recv { $self->{debug} >= 6 && _debug_dump $self '_a_recv: ', $buffer; } if(defined $res && $res == 0 && $self->{_to_read}) { + $self->{debug} <= 0 || _debug $self "$!" and next if $!{EINTR}; $self->{_error} .= "Server unexpectedly closed connection ($self->{_to_read} bytes unread)"; return; } if(!defined $res) { + $self->{debug} <= 0 || _debug $self "$!" and next if $!{EINTR}; $self->{_timedout} = !!$!{EAGAIN}; $self->{_error} .= $! unless $self->{_async} && $!{EAGAIN}; return; @@ -277,12 +258,11 @@ sub _a_close { $error = '' unless defined $error; $self->{_timedout} ||= $error eq 'timeout'; $self->{_error} ||= $error; - _stats($self->{_start_time}, $self->{_server}); my $ret; if ($self->{_error} || $self->{_timedout}) { _close_sock $self $self->{_server}; # something went wrong, close socket just in case $self->{_error} = "Timeout ($self->{_error})" if $self->{_timedout}; - $self->{debug} >= 0 && _debug $self "failed with: $self->{_error}"; + $self->{debug} >= 1 && _debug $self "failed with: $self->{_error}"; $ret = { fail => $self->{_error}, timeout => $self->{_timedout} }; } else { $self->{debug} >= 5 && _debug_dump $self '_unpack_body: ', $self->{_buf}; @@ -322,8 +302,11 @@ sub _a_clear { delete $self->{_write_buf}; } -sub _select_server -{ +sub _n_servers { + return scalar @{$_[0]->{servers}}; +} + +sub _select_server { my ($self, $key) = @_; my $n = @{$self->{servers}}; while($n--) { @@ -335,23 +318,24 @@ sub _select_server $self->{current_server} = $self->_balance_hash($key); } last unless $self->{current_server}; - $self->_mark_server_bad if mPOP::RemoteStorTimeOut("iproto:$self->{current_server}->{repr}"); last if $self->{current_server}; } + if($self->{current_server}) { + $self->{_last_server} = $self->{current_server}->{repr}; + } return $self->{current_server}; } -sub _mark_server_bad -{ - my ($self) = @_; +sub _mark_server_bad { + my ($self, $remove_last_server) = @_; if ($self->{balance} == HASH()) { delete($self->{current_server}->{ok}); } delete($self->{current_server}); + $self->{_last_server} = '' if $remove_last_server; } -sub _balance_rr -{ +sub _balance_rr { my ($self) = @_; if (scalar(@{$self->{servers}}) == 1) { return $self->{servers}->[0]; @@ -361,8 +345,7 @@ sub _balance_rr } } -sub _balance_hash -{ +sub _balance_hash { my ($self, $key) = @_; my ($hash_, $hash, $server); @@ -376,8 +359,7 @@ sub _balance_hash return $self->{servers}->[rand @{$self->{servers}}]; #last resort } -sub _balance_ketama -{ +sub _balance_ketama { my ($self, $key) = @_; my $idx = crc32($key); @@ -398,8 +380,19 @@ sub _set_sock_timeout ($$) { # not a class method! ); } -sub _connect -{ +sub _set_blocking ($$) { + my ($sock, $blocking) = @_; + my $flags = 0; + fcntl($sock, F_GETFL, $flags) or die $!; + if($blocking) { + $flags &= ~O_NONBLOCK; + } else { + $flags |= O_NONBLOCK; + } + fcntl($sock, F_SETFL, $flags) or die $!; +} + +sub _connect { my ($self, $server, $err, $async) = @_; $self->{_timedout} = 0; @@ -412,24 +405,36 @@ sub _connect socket($sock, PF_INET, SOCK_STREAM, $proto); }; - _set_sock_timeout $sock, $self->{timeout_timeval} unless $async; - IO::Handle::blocking($sock,0) if $async; + if ($async) { + _set_blocking($sock,0); + } else { + _set_sock_timeout $sock, $self->{timeout_timeval}; + } my $sin = Socket::sockaddr_in($server->{'port'}, Socket::inet_aton($server->{'host'})); - unless(connect($sock, $sin)) { - $self->{_timedout} = !!$!{EINPROGRESS}; - if(!$async || !$!{EINPROGRESS}) { - $$err .= "cannot connect: $!"; - close $sock; - return undef; + while(1) { + local $! = 0; + unless(connect($sock, $sin)) { + $self->{debug} <= 0 || _debug $self "$!" and next if $!{EINTR}; + $self->{_timedout} = !!$!{EINPROGRESS}; + if (!$async || !$!{EINPROGRESS}) { + $$err .= "cannot connect: $!"; + close $sock; + return undef; + } } + last; } if($self->{tcp_nodelay}) { setsockopt($sock, $PROTO_TCP, TCP_NODELAY, 1); } - $self->{debug} >= 1 && _debug $self "connected"; + if($self->{tcp_keepalive}) { + setsockopt($sock, SOL_SOCKET, SO_KEEPALIVE, 1); + } + + $self->{debug} >= 2 && _debug $self "connected"; return $sockets{$server->{repr}} = $sock; } @@ -439,7 +444,7 @@ sub SetTimeout { my $sec = int $timeout; # seconds my $usec = int( ($timeout - $sec) * 1_000_000 ); # micro-seconds - $self->{timeout_timeval} = pack "LL", $sec, $usec; # struct timeval; + $self->{timeout_timeval} = pack "L!L!", $sec, $usec; # struct timeval; _set_sock_timeout $sockets{$_}, $self->{timeout_timeval} for @@ -448,29 +453,27 @@ sub SetTimeout { @{ $self->{servers} }; } -sub _close_sock -{ +sub _close_sock { my ($self, $server) = @_; if ($sockets{$server->{repr}}) { - $self->{debug} >= 1 && _debug $self 'closing socket'; + $self->{debug} >= 2 && _debug $self 'closing socket'; close $sockets{$server->{repr}}; delete $sockets{$server->{repr}}; } } -sub _debug -{ +sub _debug { my ($self, $msg)= @_; - my $server = $self->{current_server} && $self->{current_server}->{repr}; + my $server = $self->{current_server} && $self->{current_server}->{repr} || $self->{_last_server} || ''; my $sock = $self->{sock} || 'none'; $server &&= "$server($sock) "; - warn("MR::IProto: $server$msg\n"); + warn("$self->{name}: $server$msg\n"); + 1; } -sub _debug_dump -{ +sub _debug_dump { my ($self, $msg, $datum) = @_; my $server = $self->{current_server} && $self->{current_server}->{repr}; my $sock = $self->{sock} || 'none'; @@ -481,35 +484,37 @@ sub _debug_dump $msg .= ' > '; } $msg .= join(' ', map { sprintf "%02x", $_ } unpack("C*", $datum)); - warn("MR::IProto: $server$msg\n"); + warn("$self->{name}: $server$msg\n"); } -1; - package MR::IProto::Async; -use Carp qw/confess/; use Time::HiRes qw/time/; use List::Util qw/min shuffle/; -use Data::Dumper; -use MR::IProto (); sub DEFAULT_TIMEOUT() { 10 } sub DEFAULT_TIMEOUT_SINGLE() { 4 } sub DEFAULT_TIMEOUT_CONNECT() { 1 } - sub DEFAULT_RETRY() { 3 } +sub IPROTO_CLASS() { 'MR::IProto' } + +use overload '""' => sub { $_[0]->{name}.'[async]' }; + +BEGIN { *confess = \&MR::IProto::confess } sub new { my ($class, %opt) = @_; + ($opt{name}) = caller unless $opt{name}; my $self = bless { - timeout => $opt{timeout} || DEFAULT_TIMEOUT(), # over-all-requests timeout - timeout_single => $opt{timeout_single} || DEFAULT_TIMEOUT_SINGLE(), # per-request timeout - timeout_connect => $opt{timeout_connect} || DEFAULT_TIMEOUT_CONNECT(), # timeout to do connect() - max_request_retries => $opt{max_request_retries} || DEFAULT_RETRY(), - debug => defined($opt{debug}) ? $opt{debug} : mPOP::Config::GetValue('IProtoDebug'), + name => $opt{name}, + iproto_class => $opt{iproto_class} || $class->IPROTO_CLASS, + timeout => $opt{timeout} || $class->DEFAULT_TIMEOUT(), # over-all-requests timeout + timeout_single => $opt{timeout_single} || $class->DEFAULT_TIMEOUT_SINGLE(), # per-request timeout + timeout_connect => $opt{timeout_connect} || $class->DEFAULT_TIMEOUT_CONNECT(), # timeout to do connect() + max_request_retries => $opt{max_request_retries} || $class->DEFAULT_RETRY(), + debug => $opt{debug} || 0, servers => [ ], requests => [ @{$opt{requests}||[]} ], - nreqs => 0, + nreqs => $opt{requests} ? scalar(@{$opt{requests}}) : 0, servers_used => {}, nserver => 0, working => {}, @@ -615,10 +620,10 @@ sub Results { } my $t9 = time; - warn sprintf "MR::IProto::Async: fetched %d requests for %.4f sec (%d/%d done)\n", $self->{nreqs}, $t9-$t0, scalar@done, $nreqs; + warn sprintf "$self->{name}: fetched %d requests for %.4f sec (%d/%d done)\n", $self->{nreqs}, $t9-$t0, scalar@done, $nreqs; if(%$working) { - warn "MR::IProto::Async: ${\scalar values %$working} requests not fetched\n"; + warn "$self->{name}: ${\scalar values %$working} requests not fetched\n"; $_->{_conn}->_a_close($err) for values %$working; # warn "return nothing\n"; # return; @@ -643,7 +648,7 @@ sub _init_req { $req->{_server} = undef; my $server = _select_server $self or return '0E0'; - my $conn = MR::IProto->new({ + my $conn = $self->{iproto_class}->new({ servers => $server->{repr}, debug => $self->{debug}, }); @@ -662,11 +667,12 @@ sub _init_req { ++$req->{_try}; ++$self->{nreqs}; - return $conn; + return $conn && 1; } sub _select_server { - my ($self) = @_; + my ($self, $disable_servers) = @_; + $disable_servers ||= {}; my $servers = $self->{servers}; my $servers_used = $self->{servers_used}; @@ -676,7 +682,7 @@ sub _select_server { while($i <= @$servers) { my $repr = $servers->[($i+$nserver)%@$servers]->{repr}; next if $servers_used->{$repr}; - next if mPOP::RemoteStorTimeOut("iproto:$repr"); + next if $disable_servers->{$repr}; last; } continue { ++$i; @@ -714,10 +720,7 @@ sub _parse_servers { sub _die { my ($self, @e) = @_; - local $Data::Dumper::Terse = 1; - local $Data::Dumper::Indent = 2; - local $Data::Dumper::Maxdepth = 0; - confess join('; ', @e, Dumper($self)); + die "$self->{name}: ".join('; ', @e); } 1; diff --git a/mod/silverbox/client/perl/MR/SilverBox.pm b/mod/silverbox/client/perl/lib/MR/SilverBox.pm similarity index 65% rename from mod/silverbox/client/perl/MR/SilverBox.pm rename to mod/silverbox/client/perl/lib/MR/SilverBox.pm index 8177b765f1d75877c35545bd38c382e4e3047223..76d350d7adc1888782bf2ac16981a87b8c680b5f 100644 --- a/mod/silverbox/client/perl/MR/SilverBox.pm +++ b/mod/silverbox/client/perl/lib/MR/SilverBox.pm @@ -1,38 +1,48 @@ -# $Id: SilverBox.pm,v 1.49 2010/06/18 08:52:42 nevinitsin Exp $ package MR::SilverBox; use strict; use warnings; -use MR::IProto (); -use MR::Storage::Const (); use Scalar::Util qw/looks_like_number/; use List::MoreUtils qw/each_arrayref/; -use Carp qw/confess/; -use constant WANT_RESULT => 1; -use constant UPDATE_BASE => 2; -use constant UPDATE_FLAGS => 4; +use MR::IProto (); +use MR::Storage::Const (); + +use constant { + WANT_RESULT => 1, + INSERT_ADD => 2, + INSERT_REPLACE => 4, +}; + +sub IPROTOCLASS () { 'MR::IProto' } +sub ERRSTRCLASS () { 'MR::Storage::Const::Errors::SilverBox' } -sub new -{ +use vars qw/$VERSION/; +$VERSION = 0; + +BEGIN { *confess = \&MR::IProto::confess } + +sub new { my ($class, $arg) = @_; my $self; $arg = { %$arg }; - $self->{name} = $arg->{name} || ref$class || $class; - $self->{timeout} = $arg->{timeout} || 23; - $self->{retry } = $arg->{retry} || 1; - $self->{softretry} = $arg->{softretry} || 3; - $self->{debug} = $arg->{'debug'} || 0; - $self->{ipdebug} = $arg->{'ipdebug'} || 0; - $self->{raise} = 1; - $self->{raise} = $arg->{raise} if exists $arg->{raise}; -# $self->{default_namespace} = exists $arg->{default_namespace} ? $arg->{default_namespace} : 0; - $self->{default_raw} = exists $arg->{default_raw} ? $arg->{default_raw} : 0; - $self->{hashify} = $arg->{'hashify'} if exists $arg->{'hashify'}; -# $self->{string_key} = exists $arg->{string_key} ? $arg->{string_key} : { 1 => 'string' }; - $self->{select_timeout} = $arg->{select_timeout} || $self->{timeout}; + $self->{name} = $arg->{name} || ref$class || $class; + $self->{timeout} = $arg->{timeout} || 23; + $self->{retry } = $arg->{retry} || 1; + $self->{select_retry} = $arg->{select_retry} || 3; + $self->{softretry} = $arg->{softretry} || 3; + $self->{debug} = $arg->{'debug'} || 0; + $self->{ipdebug} = $arg->{'ipdebug'} || 0; + $self->{raise} = 1; + $self->{raise} = $arg->{raise} if exists $arg->{raise}; + $self->{hashify} = $arg->{'hashify'} if exists $arg->{'hashify'}; + $self->{default_raw} = exists $arg->{default_raw} ? $arg->{default_raw} : !$self->{hashify}; + $self->{select_timeout} = $arg->{select_timeout} || $self->{timeout}; + $self->{iprotoclass} = $arg->{iprotoclass} || $class->IPROTOCLASS; + $self->{errstrclass} = $arg->{errstrclass} || $class->ERRSTRCLASS; + $self->{_last_error} = 0; $arg->{namespaces} = [@{ $arg->{namespaces} }]; my %namespaces; @@ -47,8 +57,9 @@ sub new confess "ns[$namespace] bad format `$ns->{format}'" if $ns->{format} =~ m/[^&lLsScC ]/; $ns->{format} =~ s/\s+//g; my @f = split //, $ns->{format}; - $ns->{unpack_format} = join('', map { /&/ ? 'w/a*' : "x$_" } @f ); - $ns->{field_format} = [ map { /&/ ? 'a*' : $_ } @f ]; + $ns->{byfield_unpack_format} = [ map { /&/ ? 'w/a*' : "x$_" } @f ]; + $ns->{field_format} = [ map { /&/ ? 'a*' : $_ } @f ]; + $ns->{unpack_format} = join('', @{$ns->{byfield_unpack_format}}); $ns->{append_for_unpack} = '' unless defined $ns->{append_for_unpack}; $ns->{check_keys} = {}; $ns->{string_keys} = { map { $_ => 1 } grep { $f[$_] eq '&' } 0..$#f }; @@ -96,25 +107,36 @@ sub _debug { sub _connect { my ($self, $servers) = @_; + $self->{server} = $self->{iprotoclass}->new({ + servers => $servers, + name => $self->{name}, + debug => $self->{'ipdebug'}, + dump_no_ints => 1, + }); +} - $self->{'server'} = MR::IProto->new({ servers => $servers, - debug => $self->{'ipdebug'}, - dump_no_ints => 1 }); +sub ErrorStr { + my ($self, $code) = @_; + return $self->{_last_error_msg} if $self->{_last_error} eq 'fail'; + return $self->{errstrclass}->ErrorStr($code || $self->{_last_error}); +} + +sub Error { + return $_[0]->{_last_error}; } sub _chat { my ($self, %param) = @_; my $orig_unpack = delete $param{unpack}; - my $soft_error = {}; # exception, any ref will do $param{unpack} = sub { my $data = $_[0]; confess __LINE__."$self->{name}: [common]: Bad response" if length $data < 4; - my @err_code = unpack('CCCC', substr($data, 0, 4, '')); + my ($full_code, @err_code) = unpack('LX[L]CCCC', substr($data, 0, 4, '')); # $err_code[0] = severity: 0 -> ok, 1 -> transient, 2 -> permanent; # $err_code[1] = description; # $err_code[3] = da box project; - return (\@err_code, \$data); + return (\@err_code, \$data, $full_code); }; my $timeout = $param{timeout} || $self->{timeout}; @@ -125,31 +147,37 @@ sub _chat { while ($retry > 0) { $retry_count++; + $self->{_last_error} = 0x77777777; $self->{server}->SetTimeout($timeout); my $ret = $self->{server}->Chat1(%param); my $message; if (exists $ret->{ok}) { - my ($ret_code, $data) = @{$ret->{ok}}; + my ($ret_code, $data, $full_code) = @{$ret->{ok}}; + $self->{_last_error} = $full_code; if ($ret_code->[0] == 0) { my $ret = $orig_unpack->($$data,$ret_code->[3]); confess __LINE__."$self->{name}: [common]: Bad response (more data left)" if length $$data > 0; return $ret; } - my $full_code = ($ret_code->[1] << 8) + $ret_code->[0]; - $message = MR::Storage::Const::Errors::SilverBox->ErrorStr($full_code); - confess "$self->{name}: $message" if $ret_code->[0] == 2; #fatal error - $self->_debug("$self->{name}: $message"); + $message = $self->{errstrclass}->ErrorStr($full_code); + $self->_debug("$self->{name}: $message") if $self->{debug} >= 1; + if ($ret_code->[0] == 2) { #fatal error + $self->_raise($message) if $self->{raise}; + return 0; + } # retry if error is soft even in case of update e.g. ROW_LOCK if ($ret_code->[0] == 1 and --$soft_retry > 0) { --$retry if $retry > 1; + sleep 1; next; } - } else { - $message ||= $ret->{fail} || $ret->{timeout}; - $self->_debug("$self->{name}: $message"); + } else { # timeout has caused the failure if $ret->{timeout} + $self->{_last_error} = 'fail'; + $message ||= $self->{_last_error_msg} = $ret->{fail}; + $self->_debug("$self->{name}: $message") if $self->{debug} >= 1; } last unless --$retry; @@ -157,16 +185,20 @@ sub _chat { sleep 1; }; - confess "$self->{name}: no success after $retry_count tries\n" if $self->{raise}; + $self->_raise("no success after $retry_count tries\n") if $self->{raise}; +} + +sub _raise { + my ($self, $msg) = @_; + die "$self->{name}: $msg"; } -sub _validate_param -{ +sub _validate_param { my ($self, $args, @pnames) = @_; my $param = ref $args->[-1] eq 'HASH' ? pop @$args: {}; foreach my $pname (keys %$param) { - confess "$self->{name}: unknown param $pname\n" if grep { $_ eq $pname } @pnames == 0; + confess "$self->{name}: unknown param $pname\n" if 0 == grep { $_ eq $pname } @pnames; } $param->{namespace} = $self->{default_namespace} unless defined $param->{namespace}; @@ -178,17 +210,39 @@ sub _validate_param return ($param, map { /namespace/ ? $self->{namespaces}->{$param->{namespace}} : $param->{$_} } @pnames); } +sub Add { # store tuple if tuple identified by primary key _does_not_ exist + my $param = @_ && ref $_[-1] eq 'HASH' ? pop : {}; + $param->{action} = 'add'; + $_[0]->Insert(@_[1..$#_], $param); +} + +sub Set { # store tuple _anyway_ + my $param = @_ && ref $_[-1] eq 'HASH' ? pop : {}; + $param->{action} = 'set'; + $_[0]->Insert(@_[1..$#_], $param); +} -sub Insert -{ - my ($param, $namespace) = $_[0]->_validate_param(\@_, qw/namespace update_base/); +sub Replace { # store tuple if tuple identified by primary key _does_ exist + my $param = @_ && ref $_[-1] eq 'HASH' ? pop : {}; + $param->{action} = 'replace'; + $_[0]->Insert(@_[1..$#_], $param); +} + +sub Insert { + my ($param, $namespace) = $_[0]->_validate_param(\@_, qw/namespace _flags action/); my ($self, @tuple) = @_; $self->_debug("$self->{name}: INSERT(@{[map {qq{`$_'}} @tuple]})") if $self->{debug} >= 3; - my $flags = 0; - $flags |= UPDATE_BASE if $param->{update_base}; - + my $flags = $param->{_flags} || 0; + $param->{action} ||= 'set'; + if ($param->{action} eq 'add') { + $flags |= INSERT_ADD; + } elsif ($param->{action} eq 'replace') { + $flags |= INSERT_REPLACE; + } elsif ($param->{action} ne 'set') { + confess "$self->{name}: Bad insert action `$param->{action}'"; + } my $chkkey = $namespace->{check_keys}; my $fmt = $namespace->{field_format}; for (0..$#tuple) { @@ -259,8 +313,7 @@ sub _unpack_affected { } sub NPRM () { 3 } -sub _pack_keys -{ +sub _pack_keys { my ($self, $ns, $idx) = @_; my $keys = $idx->{keys}; @@ -296,37 +349,20 @@ sub _PackSelect { my ($self, $param, $namespace, @keys) = @_; return '' unless @keys; $self->_pack_keys($namespace, $param->{index}, @keys); - return pack("LLLLL a*", $namespace->{namespace}, $param->{index}->{id}, $param->{offset} || 0, $param->{limit} || scalar(@keys), scalar(@keys), join('',@keys)); -} - -sub SelectUnion { - my ($param) = $_[0]->_validate_param(\@_, qw/raw/); - my ($self, @reqs) = @_; - return [] unless @reqs; - confess "bad param" if grep { ref $_ ne 'ARRAY' } @reqs; - $param->{raw} ||= $self->{default_raw}; - $param->{want} ||= 0; - for my $req (@reqs) { - my ($param, $namespace) = $self->_validate_param($req, qw/namespace use_index raw limit offset/); - $req = { - payload => $self->_PackSelect($param, $namespace, $req), - param => $param, - namespace => $namespace, - }; - } - my $r = $self->_chat( - msg => 18, - payload => pack("L (a*)*", scalar(@reqs), map { $_->{payload} } @reqs), - unpack => sub { $self->_unpack_select_multi([map { $_->{namespace} } @reqs], "SMULTI", @_) }, - retry => 3, - timeout => $param->{timeout} || $self->{select_timeout}, - ) or return; - confess __LINE__."$self->{name}: something wrong" if @$r != @reqs; - my $ea = each_arrayref $r, \@reqs; - while(my ($res, $req) = $ea->()) { - $self->_PostSelect($res, { hashify => $req->{namespace}->{hashify}||$self->{hashify}, %$param, %{$req->{param}}, namespace => $req->{namespace} }); + my $format = ""; + if ($param->{format}) { + my $f = $namespace->{byfield_unpack_format}; + $param->{unpack_format} = join '', map { $f->[$_->{field}] } @{$param->{format}}; + $format = pack 'l*', scalar @{$param->{format}}, map { + if($_->{full}) { + $_->{offset} = 0; + $_->{length} = 'max'; + } + $_->{length} = 0x7FFFFFFF if $_->{length} eq 'max'; + @$_{qw/field offset length/} + } @{$param->{format}}; } - return $r; + return pack("LLLL a* La*", $namespace->{namespace}, $param->{index}->{id}, $param->{offset} || 0, $param->{limit} || scalar(@keys), $format, scalar(@keys), join('',@keys)); } sub _PostSelect { @@ -336,14 +372,15 @@ sub _PostSelect { } } -sub Select -{ +my @select_param_ok = qw/namespace use_index raw want next_rows limit offset raise hashify timeout format hash_by/; +sub Select { confess q/Select isnt callable in void context/ unless defined wantarray; - my ($param, $namespace) = $_[0]->_validate_param(\@_, qw/namespace use_index raw want next_rows limit offset/); + my ($param, $namespace) = $_[0]->_validate_param(\@_, @select_param_ok); my ($self, @keys) = @_; - @keys = @{$keys[0]} if ref $keys[0] eq 'ARRAY'; + local $self->{raise} = $param->{raise} if defined $param->{raise}; + @keys = @{$keys[0]} if ref $keys[0] eq 'ARRAY' and 1 == @{$param->{index}->{keys}} || ref $keys[0]->[0] eq 'ARRAY'; - $self->_debug("$self->{name}: SELECT($namespace->{namespace})[@{[map{ref$_?qq{[@$_]}:$_}@keys]}]") if $self->{debug} >= 3; + $self->_debug("$self->{name}: SELECT($namespace->{namespace}/$param->{use_index})[@{[map{ref$_?qq{[@$_]}:$_}@keys]}]") if $self->{debug} >= 3; my ($msg,$payload); if(exists $param->{next_rows}) { @@ -352,25 +389,43 @@ sub Select $self->_pack_keys($namespace, $param->{index}, @keys); $payload = pack("LL a*", $namespace->{namespace}, $param->{next_rows}, join('',@keys)), } else { - $msg = 17; - $payload = $self->_PackSelect($param, $namespace, @keys) or return []; + $payload = $self->_PackSelect($param, $namespace, @keys); + $msg = $param->{format} ? 21 : 17; } + local $namespace->{unpack_format} = $param->{unpack_format} if $param->{unpack_format}; + my $r = []; - if (@keys) { + if (@keys && $payload) { $r = $self->_chat( msg => $msg, payload => $payload, unpack => sub { $self->_unpack_select($namespace, "SELECT", @_) }, - retry => 3, + retry => $self->{select_retry}, timeout => $param->{timeout} || $self->{select_timeout}, - ); + ) or return; } - $param->{raw} ||= $self->{default_raw}; + $param->{raw} = $self->{default_raw} unless exists $param->{raw}; $param->{want} ||= !1; - $self->_PostSelect($r, { hashify => $namespace->{hashify}||$self->{hashify}, %$param, namespace => $namespace }); + $self->_PostSelect($r, { hashify => $param->{hashify}||$namespace->{hashify}||$self->{hashify}, %$param, namespace => $namespace }); + + if(defined(my $p = $param->{hash_by})) { + my %h; + if(@$r) { + if (ref $r->[0] eq 'HASH') { + confess "Bad hash_by `$p' for HASH" unless exists $r->[0]->{$p}; + $h{$_->{$p}} = $_ for @$r; + } elsif(ref $r->[0] eq 'ARRAY') { + confess "Bad hash_by `$p' for ARRAY" unless $p =~ m/^\d+$/ && $p >= 0 && $p < @{$r->[0]}; + $h{$_->[$p]} = $_ for @$r; + } else { + confess "i dont know how to hash_by ".ref($r->[0]); + } + } + return \%h; + } return $r if $param->{want} eq 'arrayref'; @@ -382,8 +437,39 @@ sub Select } } -sub Delete -{ +sub SelectUnion { + confess "not supported yet"; + my ($param) = $_[0]->_validate_param(\@_, qw/raw raise/); + my ($self, @reqs) = @_; + return [] unless @reqs; + local $self->{raise} = $param->{raise} if defined $param->{raise}; + confess "bad param" if grep { ref $_ ne 'ARRAY' } @reqs; + $param->{raw} = $self->{default_raw} unless exists $param->{raw}; + $param->{want} ||= 0; + for my $req (@reqs) { + my ($param, $namespace) = $self->_validate_param($req, @select_param_ok); + $req = { + payload => $self->_PackSelect($param, $namespace, $req), + param => $param, + namespace => $namespace, + }; + } + my $r = $self->_chat( + msg => 18, + payload => pack("L (a*)*", scalar(@reqs), map { $_->{payload} } @reqs), + unpack => sub { $self->_unpack_select_multi([map { $_->{namespace} } @reqs], "SMULTI", @_) }, + retry => $self->{select_retry}, + timeout => $param->{timeout} || $self->{select_timeout}, + ) or return; + confess __LINE__."$self->{name}: something wrong" if @$r != @reqs; + my $ea = each_arrayref $r, \@reqs; + while(my ($res, $req) = $ea->()) { + $self->_PostSelect($res, { hashify => $req->{namespace}->{hashify}||$self->{hashify}, %$param, %{$req->{param}}, namespace => $req->{namespace} }); + } + return $r; +} + +sub Delete { my ($param, $namespace) = $_[0]->_validate_param(\@_, qw/namespace/); my ($self, $key) = @_; @@ -404,6 +490,7 @@ sub OP_ADD () { 1 } sub OP_AND () { 2 } sub OP_XOR () { 3 } sub OP_OR () { 4 } +sub OP_SPLICE () { 5 } my %update_ops = ( set => OP_SET, @@ -411,16 +498,49 @@ my %update_ops = ( and => OP_AND, xor => OP_XOR, or => OP_OR, + splice => sub { + confess "value for operation splice must be an ARRAYREF of <int[, int[, string]]>" if ref $_[0] ne 'ARRAY' || @{$_[0]} < 1; + $_[0]->[0] = 0x7FFFFFFF unless defined $_[0]->[0]; + $_[0]->[0] = pack 'l', $_[0]->[0]; + $_[0]->[1] = defined $_[0]->[1] ? pack 'l', $_[0]->[1] : ''; + $_[0]->[2] = '' unless defined $_[0]->[2]; + return (OP_SPLICE, [ pack '(w/a)*', @{$_[0]} ]); + }, + append => sub { splice => [undef, 0, $_[0]] }, + prepend => sub { splice => [0, 0, $_[0]] }, + cutbeg => sub { splice => [0, $_[0], '' ] }, + cutend => sub { splice => [-$_[0], $_[0], '' ] }, + substr => 'splice', ); +!ref $_ && m/^\D/ and $_ = $update_ops{$_} || die "bad link" for values %update_ops; + my %update_arg_fmt = ( (map { $_ => 'l' } OP_ADD), (map { $_ => 'L' } OP_AND, OP_XOR, OP_OR), ); -sub UpdateMulti -{ - my ($param, $namespace) = $_[0]->_validate_param(\@_, qw/namespace want_result update_base update_flags/); +my %ops_type = ( + (map { $_ => 'any' } OP_SET), + (map { $_ => 'number' } OP_ADD, OP_AND, OP_XOR, OP_OR), + (map { $_ => 'string' } OP_SPLICE), +); + +BEGIN { + for my $op (qw/Append Prepend Cutbeg Cutend Substr/) { + eval q/ + sub /.$op.q/ { + my $param = ref $_[-1] eq 'HASH' ? pop : {}; + my ($self, $key, $field_num, $val) = @_; + $self->UpdateMulti($key, [ $field_num => /.lc($op).q/ => $val ], $param); + } + 1; + / or die $@; + } +} + +sub UpdateMulti { + my ($param, $namespace) = $_[0]->_validate_param(\@_, qw/namespace want_result _flags raw/); my ($self, $key, @op) = @_; $self->_debug("$self->{name}: UPDATEMULTI($namespace->{namespace}=$key)[@{[map{qq{[@$_]}}@op]}]") if $self->{debug} >= 3; @@ -428,17 +548,17 @@ sub UpdateMulti confess "$self->{name}\->UpdateMulti: for now key cardinality of 1 is only allowed" unless 1 == @{$param->{index}->{keys}}; confess "$self->{name}: too many op" if scalar @op > 128; - my $flags = 0; + my $flags = $param->{_flags} || 0; $flags |= WANT_RESULT if $param->{want_result}; - $flags |= UPDATE_BASE if $param->{update_base}; - $flags |= UPDATE_FLAGS if $param->{update_flags}; my $fmt = $namespace->{field_format}; foreach (@op) { confess "$self->{name}: bad op <$_>" if ref ne 'ARRAY' or @$_ != 3; my ($field_num, $op, $value) = @$_; + my $field_type = $namespace->{string_keys}->{$field_num} ? 'string' : 'number'; + my $is_array = 0; if ($op eq 'bit_set') { $op = OP_OR; } elsif ($op eq 'bit_clear') { @@ -452,9 +572,18 @@ sub UpdateMulti $op = $update_ops{$op}; } - confess "dunno what to do with ref `$value'" if ref $value; + while(ref $op eq 'CODE') { + ($op, $value) = &$op($value); + $op = $update_ops{$op} if exists $update_ops{$op}; + } + + confess "Are you sure you want to apply `$ops_type{$op}' operation to $field_type field?" if $ops_type{$op} ne $field_type && $ops_type{$op} ne 'any'; + + $value = [ $value ] unless ref $value; + confess "dunno what to do with ref `$value'" if ref $value ne 'ARRAY'; + confess "bad fieldnum: $field_num" if $field_num >= @$fmt; - $value = pack($update_arg_fmt{$op} || $fmt->[$field_num], $value); + $value = pack($update_arg_fmt{$op} || $fmt->[$field_num], @$value); $_ = pack('LCw/a*', $field_num, $op, $value); } @@ -467,15 +596,13 @@ sub UpdateMulti ); } -sub Update -{ +sub Update { my $param = ref $_[-1] eq 'HASH' ? pop : {}; my ($self, $key, $field_num, $value) = @_; $self->UpdateMulti($key, [$field_num => set => $value ], $param); } -sub AndXorAdd -{ +sub AndXorAdd { my $param = ref $_[-1] eq 'HASH' ? pop : {}; my ($self, $key, $field_num, $and, $xor, $add) = @_; my @upd; @@ -485,8 +612,7 @@ sub AndXorAdd $self->UpdateMulti($key, @upd, $param); } -sub Bit -{ +sub Bit { my $param = ref $_[-1] eq 'HASH' ? pop : {}; my ($self, $key, $field_num, %arg) = @_; confess "$self->{name}: unknown op '@{[keys %arg]}'" if grep { not /^(bit_clear|bit_set|set)$/ } keys(%arg); @@ -501,8 +627,7 @@ sub Bit $self->UpdateMulti($key, @op, $param); } -sub Num -{ +sub Num { my $param = ref $_[-1] eq 'HASH' ? pop : {}; my ($self, $key, $field_num, %arg) = @_; confess "$self->{name}: unknown op '@{[keys %arg]}'" if grep { not /^(num_add|num_sub|set)$/ } keys(%arg); diff --git a/mod/silverbox/client/perl/MR/Storage/Const.pm b/mod/silverbox/client/perl/lib/MR/Storage/Const.pm similarity index 96% rename from mod/silverbox/client/perl/MR/Storage/Const.pm rename to mod/silverbox/client/perl/lib/MR/Storage/Const.pm index dae943a5ca66ae8f1ce5986daf489024672afdc8..64f1deee76fb7439be5de49f449bac4d0fd03df0 100644 --- a/mod/silverbox/client/perl/MR/Storage/Const.pm +++ b/mod/silverbox/client/perl/lib/MR/Storage/Const.pm @@ -1,12 +1,11 @@ -# $Id: Const.pm,v 1.12 2010/06/17 13:35:13 dubravsky Exp $ - use strict; use Exporter; package MR::Storage::Const; use base qw/Exporter/; -use vars qw/@EXPORT_OK %EXPORT_TAGS %ERRORS/; +use vars qw/$VERSION @EXPORT_OK %EXPORT_TAGS %ERRORS/; +$VERSION = 0; my (@ERRORS); $EXPORT_TAGS{all} = \@EXPORT_OK; @@ -85,6 +84,8 @@ BEGIN { 0x00003400 => q{Error during iconv}, 0x00003500 => q{Event isn't contained in this node}, 0x00003600 => q{Proxy reply: destination node timed out}, + 0x00003700 => q{Node found}, + 0x00003800 => q{Index violation}, ); sub ErrorText { diff --git a/mod/silverbox/client/ruby/README b/mod/silverbox/client/ruby/README new file mode 100644 index 0000000000000000000000000000000000000000..9863c682996aec42be34cdebe8e8c03aadfd81b7 --- /dev/null +++ b/mod/silverbox/client/ruby/README @@ -0,0 +1 @@ +A toy implementation of ruby client library for IProto/Silverbox binary protocol \ No newline at end of file diff --git a/mod/silverbox/client/ruby/iproto.rb b/mod/silverbox/client/ruby/iproto.rb new file mode 100644 index 0000000000000000000000000000000000000000..03362ce88e2ee5b8a51d217369c38ec63a9734ea --- /dev/null +++ b/mod/silverbox/client/ruby/iproto.rb @@ -0,0 +1,133 @@ +# +# Copyright (C) 2009, 2010 Mail.RU +# Copyright (C) 2009, 2010 Yuriy Vostrikov +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +require 'socket' + +IPROTO_PING = 0xff00 + +include Socket::Constants + +class IProtoError < RuntimeError +end + +class IProto + @@sync = 0 + + def initialize(server, param = {}) + host, port = server.split(/:/) + @end_point = [host, port.to_i] + [:logger, :reconnect].each do |p| + instance_variable_set "@#{p}", param[p] if param.has_key? p + end + + reconnect + end + + attr_reader :sock + + def hexdump(string) + string.unpack('C*').map{ |c| "%02x" % c }.join(' ') + end + + def next_sync + @@sync += 1 + if @@sync > 0xffffffff + @@sync = 0 + end + @@sync + end + + def reconnect + @sock = TCPSocket.new(*@end_point) + @sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true) + end + + def close + @sock.close unless @sock.closed + end + + def send(message) + begin + reconnect if @sock.closed? and @reconnect + + sync = self.next_sync + payload = message[:raw] || message[:data].pack(message[:pack] || 'L*') + + buf = [message[:code], payload.bytesize, sync].pack('L3') + @logger.debug { "#{@end_point} => send hdr #{buf.unpack('L*').map{ |c| "%010i" % c }.join(' ')}" } if @logger + + buf << payload + @logger.debug { "#{@end_point} => send bdy #{hexdump(payload)}" } if @logger + + @sock.write(buf) + + header = @sock.read(12) + raise IProtoError, "can't read header" unless header + header = header.unpack('L3') + @logger.debug { "#{@end_point} => recv hdr #{header.map{ |c| "%010i" % c }.join(' ')}" } if @logger + + raise IProtoError, "response:#{header[0]} != message:#{message[:code]}" if header[0] != message[:code] + raise IProtoError, "response:#{header[2]} != message:#{sync}" if header[2] != sync + + data = @sock.read(header[1]) + @logger.debug { "#{@end_point} => recv bdy #{hexdump(data)}" } if @logger + data + rescue Exception => exc + @sock.close + raise exc + end + end + + def msg(message) + reply = send message + result = pre_process_reply message, reply + + return yield result if block_given? + result + end + + def ping + send :code => IPROTO_PING, :raw => '' + :pong + end + + def pre_process_reply(message, data) + if message[:unpack] + data.unpack(message[:unpack]) + else + data + end + end +end + +class IProtoRetCode < IProto + def pre_process_reply(message, data) + raise IProtoError, "too small response" if data.nil? or data.bytesize < 4 + ret_code = data.slice!(0, 4).unpack('L')[0] + raise IProtoError, "remote: #{'0x%x' % ret_code}" if ret_code != 0 + super message, data + end +end diff --git a/mod/silverbox/client/ruby/silverbox.rb b/mod/silverbox/client/ruby/silverbox.rb new file mode 100644 index 0000000000000000000000000000000000000000..fc7ae99dfe6128da39a99e60ed48c45cdd58654c --- /dev/null +++ b/mod/silverbox/client/ruby/silverbox.rb @@ -0,0 +1,171 @@ +# +# Copyright (C) 2009, 2010 Mail.RU +# Copyright (C) 2009, 2010 Yuriy Vostrikov +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +require 'iproto' + +class SilverBox < IProtoRetCode + BOX_RETURN_TUPLE = 0x01 + + def initialize(host, param = {}) + @namespace = param[:namespace] || 1 + super(host) + end + + attr_accessor :namespace + + def pack_field(value) + case value + when Integer + [4, value].pack('wL') + when String + fail "string too long" if value.bytesize > 1024 * 1024 + [value.bytesize, value].pack('wa*') + else + fail "unsupported field class #{value.class}" + end + end + + def pack_key(key) + [1].pack("L") + pack_field(key) + end + + # poor man emulation of perl's pack("w/a*") + def pack(values, pattern) + raw = [] + pattern.split(/\s+/).each do |fmt| + fail "not enough values given" unless values[0] + + case fmt + when 'l', 'L', 'a*', 'C' + raw << [values.shift].pack(fmt) + when /^(L|w)\/$/ + raw << [values[0].length].pack($1) + when 'field' + raw << pack_field(values.shift) + when 'field*' + values.shift.each {|x| raw << pack_field(x) } + when 'key' + raw << pack_key(values.shift) + when 'key*' + values.shift.each { |x| raw << pack_key(x) } + else + fail "unknown pack format: '#{fmt}'" + end + end + raw.join '' + end + + def unpack_field!(data) + # is there an efficient way to simulate perl's unpack("w/a") ? + byte_size = data.unpack(?w)[0] + data.slice!(0 .. [byte_size].pack(?w).bytesize - 1) + data.slice!(0 .. byte_size - 1) + end + + def unpack_tuple!(data) + tuple = [] + byte_size, cardinality = data.slice!(0 .. 7).unpack("LL") + tuple_data = data.slice!(0 .. byte_size - 1) + cardinality.times do + tuple << unpack_field!(tuple_data) + end + tuple + end + + def unpack_reply!(reply, param) + tuples_affected = reply.slice!(0 .. 3).unpack(?L)[0] + if param[:return_tuple] + tuples = [] + tuples_affected.times do + tuples << unpack_tuple!(reply) + end + tuples + else + tuples_affected + end + end + + private :pack_field, :pack_key, :pack + private :unpack_field!, :unpack_tuple!, :unpack_reply! + + def insert(tuple, param = {}) + namespace = param[:namespace] || @namespace + flags = 0 + flags |= BOX_RETURN_TUPLE if param[:return_tuple] + + reply = msg :code => 13, :raw => pack([namespace, flags, tuple], 'L L L/ field*') + unpack_reply!(reply, param) + end + + def delete(key, param = {}) + namespace = param[:namespace] || @namespace + + tuples_affected, = msg(:code => 20, :raw => pack([namespace, key], 'L key')).unpack(?L) + tuples_affected + end + + def select(*keys) + keys = keys[0] if keys[0].is_a? Array + return [] if keys.length == 0 + param = keys[-1].is_a?(Hash) ? keys.pop : {} + namespace = param[:namespace] || @namespace + offset = param[:offset] || 0 + limit = param[:limit] || -1 # UINT32_MAX actually + index = param[:index] || 0 + + reply = msg :code => 17, :raw => pack([namespace, index, offset, limit, keys], 'L L L L L/ key*') + unpack_reply!(reply, :return_tuple => true) + end + + def update_fields(key, *ops) + return [] if ops.length == 0 + param = ops[-1].is_a?(Hash) ? ops.pop : {} + namespace = param[:namespace] || @namespace + flags = 0 + flags |= BOX_RETURN_TUPLE if param[:return_tuple] + ops.map! do |op| + fail "op must be Array" unless op.is_a? Array + case op[1] + when :set + op = [op[0], 0x00, pack_field(op[2])].pack("LCa*") + when :add + op = [op[0], 0x01, 4, op[2]].pack("LCwI") + when :and + op = [op[0], 0x02, 4, op[2]].pack("LCwL") + when :or + op = [op[0], 0x03, 4, op[2]].pack("LCwL") + when :xor + op = [op[0], 0x04, 4, op[2]].pack("LCwL") + else + fail "unsupported op: '#{op[1]}'" + end + end + + reply = msg :code => 19, :raw => pack([namespace, flags, key, ops.length, ops.join('')], 'L L key L a*') + unpack_reply!(reply, param) + end +end + diff --git a/mod/silverbox/memcached.c b/mod/silverbox/memcached.c index a18a905d0893cf07bd2f92472550d5ac2e9bc4a2..f8a5ca1268f48e70cb8516204b92734b5c9c9e7b 100644 --- a/mod/silverbox/memcached.c +++ b/mod/silverbox/memcached.c @@ -42,6 +42,16 @@ #include <mod/silverbox/box.h> #include <stat.h> + +#define STAT(_) \ + _(MEMC_GET, 1) \ + _(MEMC_GET_MISS, 2) \ + _(MEMC_GET_HIT, 3) + +ENUM(memcached_stat, STAT); +STRS(memcached_stat, STAT); +int stat_base; + struct index *memcached_index; /* memcached tuple format: @@ -54,7 +64,7 @@ struct meta { } __packed__; -#line 58 "mod/silverbox/memcached.c" +#line 68 "mod/silverbox/memcached.c" static const int memcached_start = 1; static const int memcached_first_final = 197; static const int memcached_error = 0; @@ -62,7 +72,7 @@ static const int memcached_error = 0; static const int memcached_en_main = 1; -#line 57 "mod/silverbox/memcached.rl" +#line 67 "mod/silverbox/memcached.rl" @@ -126,7 +136,7 @@ delete(struct box_txn *txn, void *key) static struct box_tuple * find(void *key) { - return index_find(memcached_index, 1, key); + return memcached_index->find(memcached_index, key); } static struct meta * @@ -201,7 +211,7 @@ flush_all(void *data) { uintptr_t delay = (uintptr_t)data; fiber_sleep(delay - ev_now()); - khash_t(lstr2ptr_map) *map = memcached_index->map.str_map; + khash_t(lstr2ptr_map) *map = memcached_index->idx.str_hash; for (khiter_t i = kh_begin(map); i != kh_end(map); i++) { if (kh_exist(map, i)) { struct box_tuple *tuple = kh_value(map, i); @@ -251,12 +261,12 @@ memcached_dispatch(struct box_txn *txn) }) -#line 255 "mod/silverbox/memcached.c" +#line 265 "mod/silverbox/memcached.c" { cs = memcached_start; } -#line 260 "mod/silverbox/memcached.c" +#line 270 "mod/silverbox/memcached.c" { if ( p == pe ) goto _test_eof; @@ -314,7 +324,7 @@ case 5: goto st0; goto tr15; tr15: -#line 466 "mod/silverbox/memcached.rl" +#line 476 "mod/silverbox/memcached.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -331,7 +341,7 @@ case 5: if ( ++p == pe ) goto _test_eof6; case 6: -#line 335 "mod/silverbox/memcached.c" +#line 345 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto st7; goto st0; @@ -345,49 +355,49 @@ case 7: goto tr17; goto st0; tr17: -#line 465 "mod/silverbox/memcached.rl" +#line 475 "mod/silverbox/memcached.rl" { fstart = p; } goto st8; st8: if ( ++p == pe ) goto _test_eof8; case 8: -#line 356 "mod/silverbox/memcached.c" +#line 366 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto tr18; if ( 48 <= (*p) && (*p) <= 57 ) goto st8; goto st0; tr18: -#line 489 "mod/silverbox/memcached.rl" +#line 499 "mod/silverbox/memcached.rl" {flags = natoq(fstart, p);} goto st9; st9: if ( ++p == pe ) goto _test_eof9; case 9: -#line 370 "mod/silverbox/memcached.c" +#line 380 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto st9; if ( 48 <= (*p) && (*p) <= 57 ) goto tr21; goto st0; tr21: -#line 465 "mod/silverbox/memcached.rl" +#line 475 "mod/silverbox/memcached.rl" { fstart = p; } goto st10; st10: if ( ++p == pe ) goto _test_eof10; case 10: -#line 384 "mod/silverbox/memcached.c" +#line 394 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto tr22; if ( 48 <= (*p) && (*p) <= 57 ) goto st10; goto st0; tr22: -#line 482 "mod/silverbox/memcached.rl" +#line 492 "mod/silverbox/memcached.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) @@ -398,21 +408,21 @@ case 10: if ( ++p == pe ) goto _test_eof11; case 11: -#line 402 "mod/silverbox/memcached.c" +#line 412 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto st11; if ( 48 <= (*p) && (*p) <= 57 ) goto tr25; goto st0; tr25: -#line 465 "mod/silverbox/memcached.rl" +#line 475 "mod/silverbox/memcached.rl" { fstart = p; } goto st12; st12: if ( ++p == pe ) goto _test_eof12; case 12: -#line 416 "mod/silverbox/memcached.c" +#line 426 "mod/silverbox/memcached.c" switch( (*p) ) { case 10: goto tr26; case 13: goto tr27; @@ -422,11 +432,11 @@ case 12: goto st12; goto st0; tr26: -#line 490 "mod/silverbox/memcached.rl" +#line 500 "mod/silverbox/memcached.rl" {bytes = natoq(fstart, p);} -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 495 "mod/silverbox/memcached.rl" +#line 505 "mod/silverbox/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -447,13 +457,13 @@ case 12: goto exit; } } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 250 "mod/silverbox/memcached.rl" +#line 260 "mod/silverbox/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); @@ -464,9 +474,9 @@ case 12: } goto st197; tr30: -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 495 "mod/silverbox/memcached.rl" +#line 505 "mod/silverbox/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -487,13 +497,13 @@ case 12: goto exit; } } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 250 "mod/silverbox/memcached.rl" +#line 260 "mod/silverbox/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); @@ -504,11 +514,11 @@ case 12: } goto st197; tr39: -#line 524 "mod/silverbox/memcached.rl" +#line 534 "mod/silverbox/memcached.rl" { noreply = true; } -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 495 "mod/silverbox/memcached.rl" +#line 505 "mod/silverbox/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -529,13 +539,13 @@ case 12: goto exit; } } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 250 "mod/silverbox/memcached.rl" +#line 260 "mod/silverbox/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); @@ -546,11 +556,11 @@ case 12: } goto st197; tr58: -#line 490 "mod/silverbox/memcached.rl" +#line 500 "mod/silverbox/memcached.rl" {bytes = natoq(fstart, p);} -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 495 "mod/silverbox/memcached.rl" +#line 505 "mod/silverbox/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -571,13 +581,13 @@ case 12: goto exit; } } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 279 "mod/silverbox/memcached.rl" +#line 289 "mod/silverbox/memcached.rl" { struct tbuf *b; void *value; @@ -606,9 +616,9 @@ case 12: } goto st197; tr62: -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 495 "mod/silverbox/memcached.rl" +#line 505 "mod/silverbox/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -629,13 +639,13 @@ case 12: goto exit; } } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 279 "mod/silverbox/memcached.rl" +#line 289 "mod/silverbox/memcached.rl" { struct tbuf *b; void *value; @@ -664,11 +674,11 @@ case 12: } goto st197; tr71: -#line 524 "mod/silverbox/memcached.rl" +#line 534 "mod/silverbox/memcached.rl" { noreply = true; } -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 495 "mod/silverbox/memcached.rl" +#line 505 "mod/silverbox/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -689,13 +699,13 @@ case 12: goto exit; } } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 279 "mod/silverbox/memcached.rl" +#line 289 "mod/silverbox/memcached.rl" { struct tbuf *b; void *value; @@ -724,11 +734,11 @@ case 12: } goto st197; tr91: -#line 491 "mod/silverbox/memcached.rl" +#line 501 "mod/silverbox/memcached.rl" {cas = natoq(fstart, p);} -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 495 "mod/silverbox/memcached.rl" +#line 505 "mod/silverbox/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -749,13 +759,13 @@ case 12: goto exit; } } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 268 "mod/silverbox/memcached.rl" +#line 278 "mod/silverbox/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); @@ -768,9 +778,9 @@ case 12: } goto st197; tr95: -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 495 "mod/silverbox/memcached.rl" +#line 505 "mod/silverbox/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -791,13 +801,13 @@ case 12: goto exit; } } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 268 "mod/silverbox/memcached.rl" +#line 278 "mod/silverbox/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); @@ -810,11 +820,11 @@ case 12: } goto st197; tr105: -#line 524 "mod/silverbox/memcached.rl" +#line 534 "mod/silverbox/memcached.rl" { noreply = true; } -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 495 "mod/silverbox/memcached.rl" +#line 505 "mod/silverbox/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -835,13 +845,13 @@ case 12: goto exit; } } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 268 "mod/silverbox/memcached.rl" +#line 278 "mod/silverbox/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); @@ -854,17 +864,17 @@ case 12: } goto st197; tr118: -#line 492 "mod/silverbox/memcached.rl" +#line 502 "mod/silverbox/memcached.rl" {incr = natoq(fstart, p);} -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 306 "mod/silverbox/memcached.rl" +#line 316 "mod/silverbox/memcached.rl" { struct meta *m; struct tbuf *b; @@ -917,15 +927,15 @@ case 12: } goto st197; tr122: -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 306 "mod/silverbox/memcached.rl" +#line 316 "mod/silverbox/memcached.rl" { struct meta *m; struct tbuf *b; @@ -978,17 +988,17 @@ case 12: } goto st197; tr132: -#line 524 "mod/silverbox/memcached.rl" +#line 534 "mod/silverbox/memcached.rl" { noreply = true; } -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 306 "mod/silverbox/memcached.rl" +#line 316 "mod/silverbox/memcached.rl" { struct meta *m; struct tbuf *b; @@ -1041,15 +1051,15 @@ case 12: } goto st197; tr141: -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 357 "mod/silverbox/memcached.rl" +#line 367 "mod/silverbox/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); @@ -1064,21 +1074,21 @@ case 12: } goto st197; tr146: -#line 482 "mod/silverbox/memcached.rl" +#line 492 "mod/silverbox/memcached.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) exptime = exptime + ev_now(); } -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 357 "mod/silverbox/memcached.rl" +#line 367 "mod/silverbox/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); @@ -1093,17 +1103,17 @@ case 12: } goto st197; tr157: -#line 524 "mod/silverbox/memcached.rl" +#line 534 "mod/silverbox/memcached.rl" { noreply = true; } -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 357 "mod/silverbox/memcached.rl" +#line 367 "mod/silverbox/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); @@ -1118,15 +1128,15 @@ case 12: } goto st197; tr169: -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 447 "mod/silverbox/memcached.rl" +#line 457 "mod/silverbox/memcached.rl" { if (flush_delay > 0) { struct fiber *f = fiber_create("flush_all", -1, -1, flush_all, (void *)flush_delay); @@ -1138,17 +1148,17 @@ case 12: } goto st197; tr174: -#line 493 "mod/silverbox/memcached.rl" +#line 503 "mod/silverbox/memcached.rl" {flush_delay = natoq(fstart, p);} -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 447 "mod/silverbox/memcached.rl" +#line 457 "mod/silverbox/memcached.rl" { if (flush_delay > 0) { struct fiber *f = fiber_create("flush_all", -1, -1, flush_all, (void *)flush_delay); @@ -1160,17 +1170,17 @@ case 12: } goto st197; tr185: -#line 524 "mod/silverbox/memcached.rl" +#line 534 "mod/silverbox/memcached.rl" { noreply = true; } -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 447 "mod/silverbox/memcached.rl" +#line 457 "mod/silverbox/memcached.rl" { if (flush_delay > 0) { struct fiber *f = fiber_create("flush_all", -1, -1, flush_all, (void *)flush_delay); @@ -1182,21 +1192,21 @@ case 12: } goto st197; tr195: -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 370 "mod/silverbox/memcached.rl" +#line 380 "mod/silverbox/memcached.rl" { txn->op = SELECT; fiber_register_cleanup((void *)txn_cleanup, txn); - stat_collect("MEMC_GET", 1); + stat_collect(stat_base, MEMC_GET, 1); stats.cmd_get++; - say_debug("nesuring space for %i keys", keys->len); + say_debug("ensuring space for %"PRI_SZ" keys", keys_count); iov_ensure(keys_count * 5 + 1); while (keys_count-- > 0) { struct box_tuple *tuple; @@ -1214,7 +1224,7 @@ case 12: key_len = load_varint32(&key); if (tuple == NULL || tuple->flags & GHOST) { - stat_collect("MEMC_GET_MISS", 1); + stat_collect(stat_base, MEMC_GET_MISS, 1); stats.get_misses++; continue; } @@ -1241,11 +1251,11 @@ case 12: if (m->exptime > 0 && m->exptime < ev_now()) { stats.get_misses++; - stat_collect("MEMC_GET_MISS", 1); + stat_collect(stat_base, MEMC_GET_MISS, 1); continue; } else { stats.get_hits++; - stat_collect("MEMC_GET_HIT", 1); + stat_collect(stat_base, MEMC_GET_HIT, 1); } tuple_txn_ref(txn, tuple); @@ -1269,25 +1279,25 @@ case 12: } goto st197; tr213: -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 461 "mod/silverbox/memcached.rl" +#line 471 "mod/silverbox/memcached.rl" { return 0; } goto st197; tr233: -#line 490 "mod/silverbox/memcached.rl" +#line 500 "mod/silverbox/memcached.rl" {bytes = natoq(fstart, p);} -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 495 "mod/silverbox/memcached.rl" +#line 505 "mod/silverbox/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -1308,13 +1318,13 @@ case 12: goto exit; } } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 259 "mod/silverbox/memcached.rl" +#line 269 "mod/silverbox/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); @@ -1325,9 +1335,9 @@ case 12: } goto st197; tr237: -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 495 "mod/silverbox/memcached.rl" +#line 505 "mod/silverbox/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -1348,13 +1358,13 @@ case 12: goto exit; } } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 259 "mod/silverbox/memcached.rl" +#line 269 "mod/silverbox/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); @@ -1365,11 +1375,11 @@ case 12: } goto st197; tr246: -#line 524 "mod/silverbox/memcached.rl" +#line 534 "mod/silverbox/memcached.rl" { noreply = true; } -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 495 "mod/silverbox/memcached.rl" +#line 505 "mod/silverbox/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -1390,13 +1400,13 @@ case 12: goto exit; } } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 259 "mod/silverbox/memcached.rl" +#line 269 "mod/silverbox/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); @@ -1407,11 +1417,11 @@ case 12: } goto st197; tr263: -#line 490 "mod/silverbox/memcached.rl" +#line 500 "mod/silverbox/memcached.rl" {bytes = natoq(fstart, p);} -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 495 "mod/silverbox/memcached.rl" +#line 505 "mod/silverbox/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -1432,22 +1442,22 @@ case 12: goto exit; } } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 245 "mod/silverbox/memcached.rl" +#line 255 "mod/silverbox/memcached.rl" { key = read_field(keys); STORE; } goto st197; tr267: -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 495 "mod/silverbox/memcached.rl" +#line 505 "mod/silverbox/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -1468,24 +1478,24 @@ case 12: goto exit; } } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 245 "mod/silverbox/memcached.rl" +#line 255 "mod/silverbox/memcached.rl" { key = read_field(keys); STORE; } goto st197; tr276: -#line 524 "mod/silverbox/memcached.rl" +#line 534 "mod/silverbox/memcached.rl" { noreply = true; } -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 495 "mod/silverbox/memcached.rl" +#line 505 "mod/silverbox/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -1506,28 +1516,28 @@ case 12: goto exit; } } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 245 "mod/silverbox/memcached.rl" +#line 255 "mod/silverbox/memcached.rl" { key = read_field(keys); STORE; } goto st197; tr281: -#line 522 "mod/silverbox/memcached.rl" +#line 532 "mod/silverbox/memcached.rl" { p++; } -#line 516 "mod/silverbox/memcached.rl" +#line 526 "mod/silverbox/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 457 "mod/silverbox/memcached.rl" +#line 467 "mod/silverbox/memcached.rl" { print_stats(); } @@ -1536,33 +1546,33 @@ case 12: if ( ++p == pe ) goto _test_eof197; case 197: -#line 1540 "mod/silverbox/memcached.c" +#line 1550 "mod/silverbox/memcached.c" goto st0; tr27: -#line 490 "mod/silverbox/memcached.rl" +#line 500 "mod/silverbox/memcached.rl" {bytes = natoq(fstart, p);} goto st13; tr40: -#line 524 "mod/silverbox/memcached.rl" +#line 534 "mod/silverbox/memcached.rl" { noreply = true; } goto st13; st13: if ( ++p == pe ) goto _test_eof13; case 13: -#line 1554 "mod/silverbox/memcached.c" +#line 1564 "mod/silverbox/memcached.c" if ( (*p) == 10 ) goto tr30; goto st0; tr28: -#line 490 "mod/silverbox/memcached.rl" +#line 500 "mod/silverbox/memcached.rl" {bytes = natoq(fstart, p);} goto st14; st14: if ( ++p == pe ) goto _test_eof14; case 14: -#line 1566 "mod/silverbox/memcached.c" +#line 1576 "mod/silverbox/memcached.c" switch( (*p) ) { case 32: goto st14; case 110: goto st15; @@ -1655,18 +1665,18 @@ case 26: goto tr45; goto st0; tr45: -#line 530 "mod/silverbox/memcached.rl" +#line 540 "mod/silverbox/memcached.rl" {append = true; } goto st27; tr209: -#line 531 "mod/silverbox/memcached.rl" +#line 541 "mod/silverbox/memcached.rl" {append = false;} goto st27; st27: if ( ++p == pe ) goto _test_eof27; case 27: -#line 1670 "mod/silverbox/memcached.c" +#line 1680 "mod/silverbox/memcached.c" switch( (*p) ) { case 13: goto st0; case 32: goto st27; @@ -1675,7 +1685,7 @@ case 27: goto st0; goto tr46; tr46: -#line 466 "mod/silverbox/memcached.rl" +#line 476 "mod/silverbox/memcached.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -1692,7 +1702,7 @@ case 27: if ( ++p == pe ) goto _test_eof28; case 28: -#line 1696 "mod/silverbox/memcached.c" +#line 1706 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto st29; goto st0; @@ -1706,49 +1716,49 @@ case 29: goto tr49; goto st0; tr49: -#line 465 "mod/silverbox/memcached.rl" +#line 475 "mod/silverbox/memcached.rl" { fstart = p; } goto st30; st30: if ( ++p == pe ) goto _test_eof30; case 30: -#line 1717 "mod/silverbox/memcached.c" +#line 1727 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto tr50; if ( 48 <= (*p) && (*p) <= 57 ) goto st30; goto st0; tr50: -#line 489 "mod/silverbox/memcached.rl" +#line 499 "mod/silverbox/memcached.rl" {flags = natoq(fstart, p);} goto st31; st31: if ( ++p == pe ) goto _test_eof31; case 31: -#line 1731 "mod/silverbox/memcached.c" +#line 1741 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto st31; if ( 48 <= (*p) && (*p) <= 57 ) goto tr53; goto st0; tr53: -#line 465 "mod/silverbox/memcached.rl" +#line 475 "mod/silverbox/memcached.rl" { fstart = p; } goto st32; st32: if ( ++p == pe ) goto _test_eof32; case 32: -#line 1745 "mod/silverbox/memcached.c" +#line 1755 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto tr54; if ( 48 <= (*p) && (*p) <= 57 ) goto st32; goto st0; tr54: -#line 482 "mod/silverbox/memcached.rl" +#line 492 "mod/silverbox/memcached.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) @@ -1759,21 +1769,21 @@ case 32: if ( ++p == pe ) goto _test_eof33; case 33: -#line 1763 "mod/silverbox/memcached.c" +#line 1773 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto st33; if ( 48 <= (*p) && (*p) <= 57 ) goto tr57; goto st0; tr57: -#line 465 "mod/silverbox/memcached.rl" +#line 475 "mod/silverbox/memcached.rl" { fstart = p; } goto st34; st34: if ( ++p == pe ) goto _test_eof34; case 34: -#line 1777 "mod/silverbox/memcached.c" +#line 1787 "mod/silverbox/memcached.c" switch( (*p) ) { case 10: goto tr58; case 13: goto tr59; @@ -1783,30 +1793,30 @@ case 34: goto st34; goto st0; tr59: -#line 490 "mod/silverbox/memcached.rl" +#line 500 "mod/silverbox/memcached.rl" {bytes = natoq(fstart, p);} goto st35; tr72: -#line 524 "mod/silverbox/memcached.rl" +#line 534 "mod/silverbox/memcached.rl" { noreply = true; } goto st35; st35: if ( ++p == pe ) goto _test_eof35; case 35: -#line 1798 "mod/silverbox/memcached.c" +#line 1808 "mod/silverbox/memcached.c" if ( (*p) == 10 ) goto tr62; goto st0; tr60: -#line 490 "mod/silverbox/memcached.rl" +#line 500 "mod/silverbox/memcached.rl" {bytes = natoq(fstart, p);} goto st36; st36: if ( ++p == pe ) goto _test_eof36; case 36: -#line 1810 "mod/silverbox/memcached.c" +#line 1820 "mod/silverbox/memcached.c" switch( (*p) ) { case 32: goto st36; case 110: goto st37; @@ -1896,7 +1906,7 @@ case 47: goto st0; goto tr76; tr76: -#line 466 "mod/silverbox/memcached.rl" +#line 476 "mod/silverbox/memcached.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -1913,7 +1923,7 @@ case 47: if ( ++p == pe ) goto _test_eof48; case 48: -#line 1917 "mod/silverbox/memcached.c" +#line 1927 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto st49; goto st0; @@ -1927,49 +1937,49 @@ case 49: goto tr78; goto st0; tr78: -#line 465 "mod/silverbox/memcached.rl" +#line 475 "mod/silverbox/memcached.rl" { fstart = p; } goto st50; st50: if ( ++p == pe ) goto _test_eof50; case 50: -#line 1938 "mod/silverbox/memcached.c" +#line 1948 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto tr79; if ( 48 <= (*p) && (*p) <= 57 ) goto st50; goto st0; tr79: -#line 489 "mod/silverbox/memcached.rl" +#line 499 "mod/silverbox/memcached.rl" {flags = natoq(fstart, p);} goto st51; st51: if ( ++p == pe ) goto _test_eof51; case 51: -#line 1952 "mod/silverbox/memcached.c" +#line 1962 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto st51; if ( 48 <= (*p) && (*p) <= 57 ) goto tr82; goto st0; tr82: -#line 465 "mod/silverbox/memcached.rl" +#line 475 "mod/silverbox/memcached.rl" { fstart = p; } goto st52; st52: if ( ++p == pe ) goto _test_eof52; case 52: -#line 1966 "mod/silverbox/memcached.c" +#line 1976 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto tr83; if ( 48 <= (*p) && (*p) <= 57 ) goto st52; goto st0; tr83: -#line 482 "mod/silverbox/memcached.rl" +#line 492 "mod/silverbox/memcached.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) @@ -1980,49 +1990,49 @@ case 52: if ( ++p == pe ) goto _test_eof53; case 53: -#line 1984 "mod/silverbox/memcached.c" +#line 1994 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto st53; if ( 48 <= (*p) && (*p) <= 57 ) goto tr86; goto st0; tr86: -#line 465 "mod/silverbox/memcached.rl" +#line 475 "mod/silverbox/memcached.rl" { fstart = p; } goto st54; st54: if ( ++p == pe ) goto _test_eof54; case 54: -#line 1998 "mod/silverbox/memcached.c" +#line 2008 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto tr87; if ( 48 <= (*p) && (*p) <= 57 ) goto st54; goto st0; tr87: -#line 490 "mod/silverbox/memcached.rl" +#line 500 "mod/silverbox/memcached.rl" {bytes = natoq(fstart, p);} goto st55; st55: if ( ++p == pe ) goto _test_eof55; case 55: -#line 2012 "mod/silverbox/memcached.c" +#line 2022 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto st55; if ( 48 <= (*p) && (*p) <= 57 ) goto tr90; goto st0; tr90: -#line 465 "mod/silverbox/memcached.rl" +#line 475 "mod/silverbox/memcached.rl" { fstart = p; } goto st56; st56: if ( ++p == pe ) goto _test_eof56; case 56: -#line 2026 "mod/silverbox/memcached.c" +#line 2036 "mod/silverbox/memcached.c" switch( (*p) ) { case 10: goto tr91; case 13: goto tr92; @@ -2032,30 +2042,30 @@ case 56: goto st56; goto st0; tr106: -#line 524 "mod/silverbox/memcached.rl" +#line 534 "mod/silverbox/memcached.rl" { noreply = true; } goto st57; tr92: -#line 491 "mod/silverbox/memcached.rl" +#line 501 "mod/silverbox/memcached.rl" {cas = natoq(fstart, p);} goto st57; st57: if ( ++p == pe ) goto _test_eof57; case 57: -#line 2047 "mod/silverbox/memcached.c" +#line 2057 "mod/silverbox/memcached.c" if ( (*p) == 10 ) goto tr95; goto st0; tr93: -#line 491 "mod/silverbox/memcached.rl" +#line 501 "mod/silverbox/memcached.rl" {cas = natoq(fstart, p);} goto st58; st58: if ( ++p == pe ) goto _test_eof58; case 58: -#line 2059 "mod/silverbox/memcached.c" +#line 2069 "mod/silverbox/memcached.c" switch( (*p) ) { case 10: goto tr95; case 13: goto st57; @@ -2116,14 +2126,14 @@ case 65: } goto st0; tr107: -#line 524 "mod/silverbox/memcached.rl" +#line 534 "mod/silverbox/memcached.rl" { noreply = true; } goto st66; st66: if ( ++p == pe ) goto _test_eof66; case 66: -#line 2127 "mod/silverbox/memcached.c" +#line 2137 "mod/silverbox/memcached.c" switch( (*p) ) { case 10: goto tr95; case 13: goto st57; @@ -2161,18 +2171,18 @@ case 70: goto tr113; goto st0; tr113: -#line 539 "mod/silverbox/memcached.rl" +#line 549 "mod/silverbox/memcached.rl" {incr_sign = -1;} goto st71; tr202: -#line 538 "mod/silverbox/memcached.rl" +#line 548 "mod/silverbox/memcached.rl" {incr_sign = 1; } goto st71; st71: if ( ++p == pe ) goto _test_eof71; case 71: -#line 2176 "mod/silverbox/memcached.c" +#line 2186 "mod/silverbox/memcached.c" switch( (*p) ) { case 13: goto st0; case 32: goto st71; @@ -2181,7 +2191,7 @@ case 71: goto st0; goto tr114; tr114: -#line 466 "mod/silverbox/memcached.rl" +#line 476 "mod/silverbox/memcached.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -2198,7 +2208,7 @@ case 71: if ( ++p == pe ) goto _test_eof72; case 72: -#line 2202 "mod/silverbox/memcached.c" +#line 2212 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto st73; goto st0; @@ -2212,14 +2222,14 @@ case 73: goto tr117; goto st0; tr117: -#line 465 "mod/silverbox/memcached.rl" +#line 475 "mod/silverbox/memcached.rl" { fstart = p; } goto st74; st74: if ( ++p == pe ) goto _test_eof74; case 74: -#line 2223 "mod/silverbox/memcached.c" +#line 2233 "mod/silverbox/memcached.c" switch( (*p) ) { case 10: goto tr118; case 13: goto tr119; @@ -2229,30 +2239,30 @@ case 74: goto st74; goto st0; tr133: -#line 524 "mod/silverbox/memcached.rl" +#line 534 "mod/silverbox/memcached.rl" { noreply = true; } goto st75; tr119: -#line 492 "mod/silverbox/memcached.rl" +#line 502 "mod/silverbox/memcached.rl" {incr = natoq(fstart, p);} goto st75; st75: if ( ++p == pe ) goto _test_eof75; case 75: -#line 2244 "mod/silverbox/memcached.c" +#line 2254 "mod/silverbox/memcached.c" if ( (*p) == 10 ) goto tr122; goto st0; tr120: -#line 492 "mod/silverbox/memcached.rl" +#line 502 "mod/silverbox/memcached.rl" {incr = natoq(fstart, p);} goto st76; st76: if ( ++p == pe ) goto _test_eof76; case 76: -#line 2256 "mod/silverbox/memcached.c" +#line 2266 "mod/silverbox/memcached.c" switch( (*p) ) { case 10: goto tr122; case 13: goto st75; @@ -2313,14 +2323,14 @@ case 83: } goto st0; tr134: -#line 524 "mod/silverbox/memcached.rl" +#line 534 "mod/silverbox/memcached.rl" { noreply = true; } goto st84; st84: if ( ++p == pe ) goto _test_eof84; case 84: -#line 2324 "mod/silverbox/memcached.c" +#line 2334 "mod/silverbox/memcached.c" switch( (*p) ) { case 10: goto tr122; case 13: goto st75; @@ -2367,7 +2377,7 @@ case 89: goto st0; goto tr140; tr140: -#line 466 "mod/silverbox/memcached.rl" +#line 476 "mod/silverbox/memcached.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -2384,7 +2394,7 @@ case 89: if ( ++p == pe ) goto _test_eof90; case 90: -#line 2388 "mod/silverbox/memcached.c" +#line 2398 "mod/silverbox/memcached.c" switch( (*p) ) { case 10: goto tr141; case 13: goto st91; @@ -2392,7 +2402,7 @@ case 90: } goto st0; tr147: -#line 482 "mod/silverbox/memcached.rl" +#line 492 "mod/silverbox/memcached.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) @@ -2400,14 +2410,14 @@ case 90: } goto st91; tr158: -#line 524 "mod/silverbox/memcached.rl" +#line 534 "mod/silverbox/memcached.rl" { noreply = true; } goto st91; st91: if ( ++p == pe ) goto _test_eof91; case 91: -#line 2411 "mod/silverbox/memcached.c" +#line 2421 "mod/silverbox/memcached.c" if ( (*p) == 10 ) goto tr141; goto st0; @@ -2425,14 +2435,14 @@ case 92: goto tr144; goto st0; tr144: -#line 465 "mod/silverbox/memcached.rl" +#line 475 "mod/silverbox/memcached.rl" { fstart = p; } goto st93; st93: if ( ++p == pe ) goto _test_eof93; case 93: -#line 2436 "mod/silverbox/memcached.c" +#line 2446 "mod/silverbox/memcached.c" switch( (*p) ) { case 10: goto tr146; case 13: goto tr147; @@ -2442,7 +2452,7 @@ case 93: goto st93; goto st0; tr148: -#line 482 "mod/silverbox/memcached.rl" +#line 492 "mod/silverbox/memcached.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) @@ -2453,7 +2463,7 @@ case 93: if ( ++p == pe ) goto _test_eof94; case 94: -#line 2457 "mod/silverbox/memcached.c" +#line 2467 "mod/silverbox/memcached.c" switch( (*p) ) { case 10: goto tr141; case 13: goto st91; @@ -2514,14 +2524,14 @@ case 101: } goto st0; tr159: -#line 524 "mod/silverbox/memcached.rl" +#line 534 "mod/silverbox/memcached.rl" { noreply = true; } goto st102; st102: if ( ++p == pe ) goto _test_eof102; case 102: -#line 2525 "mod/silverbox/memcached.c" +#line 2535 "mod/silverbox/memcached.c" switch( (*p) ) { case 10: goto tr141; case 13: goto st91; @@ -2595,18 +2605,18 @@ case 111: } goto st0; tr186: -#line 524 "mod/silverbox/memcached.rl" +#line 534 "mod/silverbox/memcached.rl" { noreply = true; } goto st112; tr175: -#line 493 "mod/silverbox/memcached.rl" +#line 503 "mod/silverbox/memcached.rl" {flush_delay = natoq(fstart, p);} goto st112; st112: if ( ++p == pe ) goto _test_eof112; case 112: -#line 2610 "mod/silverbox/memcached.c" +#line 2620 "mod/silverbox/memcached.c" if ( (*p) == 10 ) goto tr169; goto st0; @@ -2624,14 +2634,14 @@ case 113: goto tr172; goto st0; tr172: -#line 465 "mod/silverbox/memcached.rl" +#line 475 "mod/silverbox/memcached.rl" { fstart = p; } goto st114; st114: if ( ++p == pe ) goto _test_eof114; case 114: -#line 2635 "mod/silverbox/memcached.c" +#line 2645 "mod/silverbox/memcached.c" switch( (*p) ) { case 10: goto tr174; case 13: goto tr175; @@ -2641,14 +2651,14 @@ case 114: goto st114; goto st0; tr176: -#line 493 "mod/silverbox/memcached.rl" +#line 503 "mod/silverbox/memcached.rl" {flush_delay = natoq(fstart, p);} goto st115; st115: if ( ++p == pe ) goto _test_eof115; case 115: -#line 2652 "mod/silverbox/memcached.c" +#line 2662 "mod/silverbox/memcached.c" switch( (*p) ) { case 10: goto tr169; case 13: goto st112; @@ -2709,14 +2719,14 @@ case 122: } goto st0; tr187: -#line 524 "mod/silverbox/memcached.rl" +#line 534 "mod/silverbox/memcached.rl" { noreply = true; } goto st123; st123: if ( ++p == pe ) goto _test_eof123; case 123: -#line 2720 "mod/silverbox/memcached.c" +#line 2730 "mod/silverbox/memcached.c" switch( (*p) ) { case 10: goto tr169; case 13: goto st112; @@ -2747,18 +2757,18 @@ case 126: } goto st0; tr191: -#line 535 "mod/silverbox/memcached.rl" +#line 545 "mod/silverbox/memcached.rl" {show_cas = false;} goto st127; tr198: -#line 536 "mod/silverbox/memcached.rl" +#line 546 "mod/silverbox/memcached.rl" {show_cas = true;} goto st127; st127: if ( ++p == pe ) goto _test_eof127; case 127: -#line 2762 "mod/silverbox/memcached.c" +#line 2772 "mod/silverbox/memcached.c" switch( (*p) ) { case 13: goto st0; case 32: goto st127; @@ -2767,7 +2777,7 @@ case 127: goto st0; goto tr193; tr193: -#line 466 "mod/silverbox/memcached.rl" +#line 476 "mod/silverbox/memcached.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -2784,7 +2794,7 @@ case 127: if ( ++p == pe ) goto _test_eof128; case 128: -#line 2788 "mod/silverbox/memcached.c" +#line 2798 "mod/silverbox/memcached.c" switch( (*p) ) { case 10: goto tr195; case 13: goto st129; @@ -2991,7 +3001,7 @@ case 155: goto st0; goto tr222; tr222: -#line 466 "mod/silverbox/memcached.rl" +#line 476 "mod/silverbox/memcached.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -3008,7 +3018,7 @@ case 155: if ( ++p == pe ) goto _test_eof156; case 156: -#line 3012 "mod/silverbox/memcached.c" +#line 3022 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto st157; goto st0; @@ -3022,49 +3032,49 @@ case 157: goto tr224; goto st0; tr224: -#line 465 "mod/silverbox/memcached.rl" +#line 475 "mod/silverbox/memcached.rl" { fstart = p; } goto st158; st158: if ( ++p == pe ) goto _test_eof158; case 158: -#line 3033 "mod/silverbox/memcached.c" +#line 3043 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto tr225; if ( 48 <= (*p) && (*p) <= 57 ) goto st158; goto st0; tr225: -#line 489 "mod/silverbox/memcached.rl" +#line 499 "mod/silverbox/memcached.rl" {flags = natoq(fstart, p);} goto st159; st159: if ( ++p == pe ) goto _test_eof159; case 159: -#line 3047 "mod/silverbox/memcached.c" +#line 3057 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto st159; if ( 48 <= (*p) && (*p) <= 57 ) goto tr228; goto st0; tr228: -#line 465 "mod/silverbox/memcached.rl" +#line 475 "mod/silverbox/memcached.rl" { fstart = p; } goto st160; st160: if ( ++p == pe ) goto _test_eof160; case 160: -#line 3061 "mod/silverbox/memcached.c" +#line 3071 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto tr229; if ( 48 <= (*p) && (*p) <= 57 ) goto st160; goto st0; tr229: -#line 482 "mod/silverbox/memcached.rl" +#line 492 "mod/silverbox/memcached.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) @@ -3075,21 +3085,21 @@ case 160: if ( ++p == pe ) goto _test_eof161; case 161: -#line 3079 "mod/silverbox/memcached.c" +#line 3089 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto st161; if ( 48 <= (*p) && (*p) <= 57 ) goto tr232; goto st0; tr232: -#line 465 "mod/silverbox/memcached.rl" +#line 475 "mod/silverbox/memcached.rl" { fstart = p; } goto st162; st162: if ( ++p == pe ) goto _test_eof162; case 162: -#line 3093 "mod/silverbox/memcached.c" +#line 3103 "mod/silverbox/memcached.c" switch( (*p) ) { case 10: goto tr233; case 13: goto tr234; @@ -3099,30 +3109,30 @@ case 162: goto st162; goto st0; tr234: -#line 490 "mod/silverbox/memcached.rl" +#line 500 "mod/silverbox/memcached.rl" {bytes = natoq(fstart, p);} goto st163; tr247: -#line 524 "mod/silverbox/memcached.rl" +#line 534 "mod/silverbox/memcached.rl" { noreply = true; } goto st163; st163: if ( ++p == pe ) goto _test_eof163; case 163: -#line 3114 "mod/silverbox/memcached.c" +#line 3124 "mod/silverbox/memcached.c" if ( (*p) == 10 ) goto tr237; goto st0; tr235: -#line 490 "mod/silverbox/memcached.rl" +#line 500 "mod/silverbox/memcached.rl" {bytes = natoq(fstart, p);} goto st164; st164: if ( ++p == pe ) goto _test_eof164; case 164: -#line 3126 "mod/silverbox/memcached.c" +#line 3136 "mod/silverbox/memcached.c" switch( (*p) ) { case 32: goto st164; case 110: goto st165; @@ -3214,7 +3224,7 @@ case 175: goto st0; goto tr252; tr252: -#line 466 "mod/silverbox/memcached.rl" +#line 476 "mod/silverbox/memcached.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -3231,7 +3241,7 @@ case 175: if ( ++p == pe ) goto _test_eof176; case 176: -#line 3235 "mod/silverbox/memcached.c" +#line 3245 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto st177; goto st0; @@ -3245,49 +3255,49 @@ case 177: goto tr254; goto st0; tr254: -#line 465 "mod/silverbox/memcached.rl" +#line 475 "mod/silverbox/memcached.rl" { fstart = p; } goto st178; st178: if ( ++p == pe ) goto _test_eof178; case 178: -#line 3256 "mod/silverbox/memcached.c" +#line 3266 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto tr255; if ( 48 <= (*p) && (*p) <= 57 ) goto st178; goto st0; tr255: -#line 489 "mod/silverbox/memcached.rl" +#line 499 "mod/silverbox/memcached.rl" {flags = natoq(fstart, p);} goto st179; st179: if ( ++p == pe ) goto _test_eof179; case 179: -#line 3270 "mod/silverbox/memcached.c" +#line 3280 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto st179; if ( 48 <= (*p) && (*p) <= 57 ) goto tr258; goto st0; tr258: -#line 465 "mod/silverbox/memcached.rl" +#line 475 "mod/silverbox/memcached.rl" { fstart = p; } goto st180; st180: if ( ++p == pe ) goto _test_eof180; case 180: -#line 3284 "mod/silverbox/memcached.c" +#line 3294 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto tr259; if ( 48 <= (*p) && (*p) <= 57 ) goto st180; goto st0; tr259: -#line 482 "mod/silverbox/memcached.rl" +#line 492 "mod/silverbox/memcached.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) @@ -3298,21 +3308,21 @@ case 180: if ( ++p == pe ) goto _test_eof181; case 181: -#line 3302 "mod/silverbox/memcached.c" +#line 3312 "mod/silverbox/memcached.c" if ( (*p) == 32 ) goto st181; if ( 48 <= (*p) && (*p) <= 57 ) goto tr262; goto st0; tr262: -#line 465 "mod/silverbox/memcached.rl" +#line 475 "mod/silverbox/memcached.rl" { fstart = p; } goto st182; st182: if ( ++p == pe ) goto _test_eof182; case 182: -#line 3316 "mod/silverbox/memcached.c" +#line 3326 "mod/silverbox/memcached.c" switch( (*p) ) { case 10: goto tr263; case 13: goto tr264; @@ -3322,30 +3332,30 @@ case 182: goto st182; goto st0; tr264: -#line 490 "mod/silverbox/memcached.rl" +#line 500 "mod/silverbox/memcached.rl" {bytes = natoq(fstart, p);} goto st183; tr277: -#line 524 "mod/silverbox/memcached.rl" +#line 534 "mod/silverbox/memcached.rl" { noreply = true; } goto st183; st183: if ( ++p == pe ) goto _test_eof183; case 183: -#line 3337 "mod/silverbox/memcached.c" +#line 3347 "mod/silverbox/memcached.c" if ( (*p) == 10 ) goto tr267; goto st0; tr265: -#line 490 "mod/silverbox/memcached.rl" +#line 500 "mod/silverbox/memcached.rl" {bytes = natoq(fstart, p);} goto st184; st184: if ( ++p == pe ) goto _test_eof184; case 184: -#line 3349 "mod/silverbox/memcached.c" +#line 3359 "mod/silverbox/memcached.c" switch( (*p) ) { case 32: goto st184; case 110: goto st185; @@ -3641,7 +3651,7 @@ case 196: _out: {} } -#line 549 "mod/silverbox/memcached.rl" +#line 559 "mod/silverbox/memcached.rl" if (!done) { @@ -3678,7 +3688,7 @@ memcached_handler(void *_data __unused__) stats.curr_connections++; int r, p; int batch_count; - int i = 0; + for (;;) { batch_count = 0; if ((r = fiber_bread(fiber->rbuf, 1)) <= 0) { @@ -3711,10 +3721,7 @@ memcached_handler(void *_data __unused__) } stats.bytes_written += r; - fiber_cleanup(); - - if (i++ % 20 == 0) - fiber_gc(); + fiber_gc(); if (p == 1 && fiber->rbuf->len > 0) { batch_count = 0; @@ -3728,11 +3735,17 @@ memcached_handler(void *_data __unused__) stats.curr_connections--; /* FIXME: nonlocal exit via exception will leak this counter */ } +void +memcached_init(void) +{ + stat_base = stat_register(memcached_stat_strs, memcached_stat_MAX); +} + void memcached_expire(void *data __unused__) { static khiter_t i; - khash_t(lstr2ptr_map) *map = memcached_index->map.str_map; + khash_t(lstr2ptr_map) *map = memcached_index->idx.str_hash; say_info("memcached expire fiber started"); for (;;) { @@ -3765,7 +3778,7 @@ memcached_expire(void *data __unused__) fiber_gc(); - double delay = cfg.memcached_expire_per_loop * cfg.memcached_expire_full_sweep / (map->size + 1); + double delay = (double)cfg.memcached_expire_per_loop * cfg.memcached_expire_full_sweep / (map->size + 1); if (delay > 1) delay = 1; fiber_sleep(delay); diff --git a/mod/silverbox/memcached.rl b/mod/silverbox/memcached.rl index 9ffe4602dba972253d3ea635322c1d8684731f93..28c64356916a3da5cc1c312e216b0a60a477cbf9 100644 --- a/mod/silverbox/memcached.rl +++ b/mod/silverbox/memcached.rl @@ -40,6 +40,16 @@ #include <mod/silverbox/box.h> #include <stat.h> + +#define STAT(_) \ + _(MEMC_GET, 1) \ + _(MEMC_GET_MISS, 2) \ + _(MEMC_GET_HIT, 3) + +ENUM(memcached_stat, STAT); +STRS(memcached_stat, STAT); +int stat_base; + struct index *memcached_index; /* memcached tuple format: @@ -117,7 +127,7 @@ delete(struct box_txn *txn, void *key) static struct box_tuple * find(void *key) { - return index_find(memcached_index, 1, key); + return memcached_index->find(memcached_index, key); } static struct meta * @@ -192,7 +202,7 @@ flush_all(void *data) { uintptr_t delay = (uintptr_t)data; fiber_sleep(delay - ev_now()); - khash_t(lstr2ptr_map) *map = memcached_index->map.str_map; + khash_t(lstr2ptr_map) *map = memcached_index->idx.str_hash; for (khiter_t i = kh_begin(map); i != kh_end(map); i++) { if (kh_exist(map, i)) { struct box_tuple *tuple = kh_value(map, i); @@ -370,9 +380,9 @@ memcached_dispatch(struct box_txn *txn) action get { txn->op = SELECT; fiber_register_cleanup((void *)txn_cleanup, txn); - stat_collect("MEMC_GET", 1); + stat_collect(stat_base, MEMC_GET, 1); stats.cmd_get++; - say_debug("nesuring space for %i keys", keys->len); + say_debug("ensuring space for %"PRI_SZ" keys", keys_count); iov_ensure(keys_count * 5 + 1); while (keys_count-- > 0) { struct box_tuple *tuple; @@ -390,7 +400,7 @@ memcached_dispatch(struct box_txn *txn) key_len = load_varint32(&key); if (tuple == NULL || tuple->flags & GHOST) { - stat_collect("MEMC_GET_MISS", 1); + stat_collect(stat_base, MEMC_GET_MISS, 1); stats.get_misses++; continue; } @@ -417,11 +427,11 @@ memcached_dispatch(struct box_txn *txn) if (m->exptime > 0 && m->exptime < ev_now()) { stats.get_misses++; - stat_collect("MEMC_GET_MISS", 1); + stat_collect(stat_base, MEMC_GET_MISS, 1); continue; } else { stats.get_hits++; - stat_collect("MEMC_GET_HIT", 1); + stat_collect(stat_base, MEMC_GET_HIT, 1); } tuple_txn_ref(txn, tuple); @@ -582,7 +592,7 @@ memcached_handler(void *_data __unused__) stats.curr_connections++; int r, p; int batch_count; - int i = 0; + for (;;) { batch_count = 0; if ((r = fiber_bread(fiber->rbuf, 1)) <= 0) { @@ -615,10 +625,7 @@ memcached_handler(void *_data __unused__) } stats.bytes_written += r; - fiber_cleanup(); - - if (i++ % 20 == 0) - fiber_gc(); + fiber_gc(); if (p == 1 && fiber->rbuf->len > 0) { batch_count = 0; @@ -632,11 +639,17 @@ exit: stats.curr_connections--; /* FIXME: nonlocal exit via exception will leak this counter */ } +void +memcached_init(void) +{ + stat_base = stat_register(memcached_stat_strs, memcached_stat_MAX); +} + void memcached_expire(void *data __unused__) { static khiter_t i; - khash_t(lstr2ptr_map) *map = memcached_index->map.str_map; + khash_t(lstr2ptr_map) *map = memcached_index->idx.str_hash; say_info("memcached expire fiber started"); for (;;) { @@ -669,7 +682,7 @@ memcached_expire(void *data __unused__) fiber_gc(); - double delay = cfg.memcached_expire_per_loop * cfg.memcached_expire_full_sweep / (map->size + 1); + double delay = (double)cfg.memcached_expire_per_loop * cfg.memcached_expire_full_sweep / (map->size + 1); if (delay > 1) delay = 1; fiber_sleep(delay); diff --git a/mod/silverbox/t/TBox.pm b/mod/silverbox/t/TBox.pm index a9cf9d4e6d0743484ab9307d144d05f2d46f4131..6bd7cdfcd4953afc4fbec2b52e743216761a72f5 100644 --- a/mod/silverbox/t/TBox.pm +++ b/mod/silverbox/t/TBox.pm @@ -1,5 +1,5 @@ use FindBin qw($Bin); -use lib "$Bin/../client/perl"; +use lib "$Bin/../client/perl/lib"; use MR::SilverBox; @@ -7,12 +7,9 @@ $main::box = MR::SilverBox->new({ servers => $ARGV[1] || q/localhost:15013/, namespaces => [ { indexes => [ { index_name => 'primary_id', - keys => [TUPLE_ID], - }, { - index_name => 'primary_email', - keys => [TUPLE_Email], - }, ], - namespace => 0, + keys => [0], + } ], + namespace => 1, format => 'l& SSLL', default_index => 'primary_id', } ], }); diff --git a/mod/silverbox/t/box.pl b/mod/silverbox/t/box.pl index 0e328fd810e9b1940e85fa1941b61f9056080ecd..29a16aad64841609eeeed5a7af525498fc481481 100644 --- a/mod/silverbox/t/box.pl +++ b/mod/silverbox/t/box.pl @@ -8,11 +8,21 @@ BEGIN { use FindBin qw($Bin); use lib "$Bin"; use TBox (); +use Carp qw/confess/; -use Test::More qw/no_plan/; +use Test::More tests => 201; use Test::Exception; -use constant ILL_PARAM => qr/MR::SilverBox: Error 00000202/; +local $SIG{__DIE__} = \&confess; + +our $CLASS; +BEGIN { $CLASS = $ENV{BOXCLASS} || 'MR::SilverBox'; eval "require $CLASS" or die $@; } + +use constant ILL_PARAM => qr/$CLASS: Error 00000202/; +use constant TUPLE_NOT_EXISTS => qr/$CLASS: Error 00003102/; +use constant TUPLE_EXISTS => qr/$CLASS: Error 00003702/; +use constant INDEX_VIOLATION => qr/$CLASS: Error 00003802/; + use constant TOO_BIG_FIELD => qr/too big field/; my $box; @@ -27,6 +37,7 @@ sub cleanup ($) { sub def_param { my $format = shift || 'l& SSLL'; return { servers => $server, + name => $CLASS, namespaces => [ { indexes => [ { index_name => 'primary_id', @@ -40,13 +51,111 @@ sub def_param { default_index => 'primary_id', } ]} } -$box = MR::SilverBox->new(def_param); -ok $box->isa('MR::SilverBox'), 'connect'; + +$box = $CLASS->new(def_param('l&SSLL&')); +ok $box->isa($CLASS), 'connect'; +cleanup 13; + +ok $box->Insert(13, q/some_email@test.mail.ru/, 1, 2, 3, 4, '777'), 'insert'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '777'], 'select/insert'; + +ok $box->Insert(13, q/some_email@test.mail.ru/, 2, 2, 3, 4, '666'), 'replace'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 2, 2, 3, 4, '666'], 'select/replace'; + +ok $box->Update(13, 3 => 1) == 1, 'update of some field'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 2, 1, 3, 4, '666'], 'select/update'; + +ok $box->Append( 13, 6 => 'APPEND') , 'append op'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 2, 1, 3, 4, '666APPEND'], 'select/append'; + +ok $box->Prepend(13, 6 => 'PREPEND'), 'prepend op'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 2, 1, 3, 4, 'PREPEND666APPEND'], 'select/prepend'; + +ok $box->Cutbeg(13, 6 => 2), 'cutbeg op'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 2, 1, 3, 4, 'EPEND666APPEND'], 'select/cutbeg'; + +ok $box->Cutend(13, 6 => 2), 'cutend op'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 2, 1, 3, 4, 'EPEND666APPE'], 'select/cutend'; + +ok $box->Substr(13, 6 => [3,4,'12345']), 'substr op'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 2, 1, 3, 4, 'EPE123456APPE'], 'select/substr'; + +ok $box->UpdateMulti(13, [6 => splice => [0]]), 'generic splice (offset = 0)'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 2, 1, 3, 4, ''], 'select/splice (offset = 0)'; + +cleanup 13; + +ok $box->Insert(13, q/some_email@test.mail.ru/, 1, 2, 3, 4, '123456789'), 'insert'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select/insert'; + +throws_ok sub { $box->UpdateMulti(13, [6 => splice => [-10]]) }, qr/Illegal parametrs/, "splice/bad_params_1"; + +ok $box->UpdateMulti(13, [6 => splice => [100]]), "splice/big_offset"; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select'; + +ok $box->UpdateMulti(13, [6 => splice => [5]]), "splice/cut_tail_pos_offset"; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '12345'], 'select'; + +ok $box->UpdateMulti(13, [6 => splice => [-2]]), "splice/cut_tail_neg_offset"; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123'], 'select'; + +ok $box->Insert(13, q/some_email@test.mail.ru/, 1, 2, 3, 4, '123456789'), 'replace'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select'; + +ok $box->UpdateMulti(13, [6 => splice => [8, 1000]]), "splice/big_pos_length"; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '12345678'], 'select'; + +ok $box->UpdateMulti(13, [6 => splice => [1, -1000]]), "splice/big_neg_length"; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '12345678'], 'select'; + +ok $box->Insert(13, q/some_email@test.mail.ru/, 1, 2, 3, 4, '123456789'), 'replace'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select'; + +ok $box->UpdateMulti(13, [6 => splice => [0x7fffffff]]), "splice/max_offset"; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select'; + +ok $box->UpdateMulti(13, [6 => splice => [1, 2]], [6 => splice => [-2, -1, 'qwe']]), "splice/multi"; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '14567qwe9'], 'select'; + + cleanup 13; +cleanup 14; +throws_ok sub { $box->Replace(13, q/some_email@test.mail.ru/, 5, 5, 5, 5, '555555555')}, TUPLE_NOT_EXISTS, 'replace'; + +ok $box->Add(13, q/some_email@test.mail.ru/, 1, 2, 3, 4, '123456789'), 'add'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select/add'; + +throws_ok sub { $box->Add(13, q/some_email@test.mail.ru/, 5, 5, 5, 5, '555555555')}, TUPLE_EXISTS, 'add2'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select/add2'; +is_deeply scalar $box->Select(q/some_email@test.mail.ru/, {use_index => "primary_email"}), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select/add2'; + +throws_ok sub { $box->Add(14, q/some_email@test.mail.ru/, 5, 5, 5, 5, '555555555')}, INDEX_VIOLATION, 'add3'; + +ok $box->Add(14, q/some1email@test.mail.ru/, 1, 2, 3, 4, '123456789'), 'add4'; +is_deeply scalar $box->Select(14), [14, 'some1email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select/add4'; +is_deeply scalar $box->Select(q/some1email@test.mail.ru/, {use_index => "primary_email"}), [14, 'some1email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select/add4'; + +throws_ok sub { $box->Replace(13, q/some1email@test.mail.ru/, 6, 6, 6, 6, '666666666')}, INDEX_VIOLATION, 'replace'; +throws_ok sub { $box->Set(13, q/some1email@test.mail.ru/, 6, 6, 6, 6, '666666666')}, INDEX_VIOLATION, 'set'; + +ok $box->Set(13, q/some_email@test.mail.ru/, 5, 5, 5, 5, '555555555'), 'set'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 5, 5, 5, 5, '555555555'], 'select/set'; + +ok $box->Replace(13, q/some_email@test.mail.ru/, 1, 2, 3, 4, '123456789'), 'replace'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select/replace'; + + + +$box = $CLASS->new(def_param); +ok $box->isa($CLASS), 'connect'; +cleanup 13; + ok $box->Insert(13, q/some_email@test.mail.ru/, 1, 2, 3, 4), 'insert'; ok $box->Insert(13, q/some_email@test.mail.ru/, 2, 2, 3, 4), 'replace'; + ok $box->Update(13, 3 => 1) == 1, 'update of some field'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 2, 1, 3, 4], 'select/update'; cleanup 14; ok $box->Insert(14, 'aaa@test.mail.ru', 0, 0, 1, 1), 'insert'; @@ -63,15 +172,15 @@ is_deeply scalar $box->Select('aaa@test.mail.ru', {use_index => 'primary_email'} -$box = MR::SilverBox->new(def_param); -ok $box->isa('MR::SilverBox'), 'connect'; +$box = $CLASS->new(def_param); +ok $box->isa($CLASS), 'connect'; for (1..3) { %MR::IProto::sockets = (); - $box = MR::SilverBox->new(def_param); - ok $box->isa('MR::SilverBox'), 'connect'; + $box = $CLASS->new(def_param); + ok $box->isa($CLASS), 'connect'; cleanup 14; @@ -108,13 +217,13 @@ $tuple[5] |= (1 << 15); $tuple[5] &= ~(1 << 16); is_deeply scalar $box->Select($id), \@tuple, 'bit_set + bit_clear'; -$box->Bit($id, 5, set => 4095, bit_set => (1 << 5), bit_clear => (1 << 6), {update_flags => 1}); +$box->Bit($id, 5, set => 4095, bit_set => (1 << 5), bit_clear => (1 << 6)); $tuple[5] = 4095; $tuple[5] |= (1 << 5); $tuple[5] &= ~(1 << 6); is_deeply scalar $box->Select($id), \@tuple, 'set + bit_set + bit_clear'; -$box->Bit($id, 5, set => 123, {update_flags => 1}); +$box->Bit($id, 5, set => 123); $tuple[5] = 123; is_deeply scalar $box->Select($id), \@tuple, 'set via Bit'; @@ -124,11 +233,11 @@ is_deeply scalar $box->Select($id), \@tuple, 'set via Bit'; ## Num ops # zero namespace -$box->Num($id, 5, num_add => 1, {update_flags => 1}); +$box->Num($id, 5, num_add => 1); $tuple[5] += 1; is_deeply scalar $box->Select($id), \@tuple, 'num_add'; -$box->Num($id, 5, num_sub => 1, {update_flags => 1}); +$box->Num($id, 5, num_sub => 1); $tuple[5] -= 1; is_deeply scalar $box->Select($id), \@tuple, 'num_sub'; @@ -152,7 +261,7 @@ throws_ok sub { $box->Num($id, 5, hxxxxx => 123) }, qr/unknown op 'hxxxxx'/, 'ba ### AndXorAdd -$box->AndXorAdd($id, 5, 4095, 5, 10, {update_flags => 1}); +$box->AndXorAdd($id, 5, 4095, 5, 10); $tuple[5] &= 4095; $tuple[5] ^= 5; $tuple[5] += 10; @@ -183,13 +292,13 @@ ok 0 == $box->Delete($id), 'delete by id'; cleanup $id; ok $box->Insert(@tuple), 'insert'; -ok $box->UpdateMulti($id, ([5 => set => 1]) x 127, {update_flags => 1}), 'big update multi'; +ok $box->UpdateMulti($id, ([5 => set => 1]) x 127), 'big update multi'; # CANT TEST throws_ok sub { $box->UpdateMulti($id, ([5 => set => 1]) x 128) }, ILL_PARAM, 'too big update multi'; throws_ok sub { $box->UpdateMulti($id, ([5 => set => 1]) x 129) }, qr/too many op/, 'too big update multi'; { - my $box = MR::SilverBox->new(def_param(q/l& SSL&/)); + my $box = $CLASS->new(def_param(q/l& SSL&/)); my @tuple = @tuple; - ok $box->isa('MR::SilverBox'), 'connect'; + ok $box->isa($CLASS), 'connect'; ok $box->UpdateMulti($id, map { [5 => set => 'x' x 127] } (1..127)), 'big update multi'; $tuple[5] = 'x' x 127; @@ -202,12 +311,12 @@ throws_ok sub { $box->UpdateMulti($id, ([5 => set => 1]) x 129) }, qr/too many o } { - my $box = MR::SilverBox->new(def_param(q/l& &&&&/)); + my $box = $CLASS->new(def_param(q/l& &&&&/)); my @tuple = @tuple; my $id = $tuple[0]; - ok $box->isa('MR::SilverBox'), 'connect'; + ok $box->isa($CLASS), 'connect'; - ok $box->UpdateMulti($id, [2 => set => 'ab'], [5 => set => 'z' x 127], {update_flags => 1}), 'update multi no teplate'; + ok $box->UpdateMulti($id, [2 => set => 'ab'], [5 => set => 'z' x 127]), 'update multi no teplate'; $tuple[2] = 'ab'; $tuple[5] = 'z' x 127; my @r = @{$box->Select($id)}; @@ -229,7 +338,7 @@ throws_ok sub { $box->UpdateMulti($id, '') }, qr/bad op/, 'bad op'; ok $box->Insert(@tuple), 'insert'; my @op = ([4 => num_add => 300], [5 => num_sub => 100], [5 => set => 1414], [5 => bit_set => 1 | 2], [5 => bit_clear => 2]); - ok $box->UpdateMulti($tuple[0], @op, {update_base => 1, update_flags => 1}), 'update multi'; + ok $box->UpdateMulti($tuple[0], @op), 'update multi'; my @tuple_new = @tuple; $tuple_new[4] += 300; $tuple_new[5] -= 100; @@ -240,9 +349,9 @@ throws_ok sub { $box->UpdateMulti($id, '') }, qr/bad op/, 'bad op'; cleanup 14; ok $box->Insert(@tuple), 'insert'; - is_deeply scalar $box->UpdateMulti($tuple[0], @op, {update_base => 1, update_flags => 1, want_result => 1}), \@tuple_new, 'update multi, want_result'; + is_deeply scalar $box->UpdateMulti($tuple[0], @op, {want_result => 1}), \@tuple_new, 'update multi, want_result'; - ok 0 == $box->UpdateMulti(15, [4 => num_add => 300], [5 => num_sub => 100], [5 => set => 1414], {update_base => 1, update_flags => 1}), 'update multi of nonexist'; + ok 0 == $box->UpdateMulti(15, [4 => num_add => 300], [5 => num_sub => 100], [5 => set => 1414]), 'update multi of nonexist'; } @@ -256,7 +365,7 @@ is_deeply [$box->Select($tuple[0], $tuple[0])], [\@tuple, \@tuple], 'select in s is_deeply scalar $box->Select($tuple[0], $tuple[0], {want => 'arrayref'}), [\@tuple, \@tuple], 'select want => arrrayref'; { - my $box = MR::SilverBox->new( + my $box = $CLASS->new( { %{def_param()}, hashify => sub { my ($namespace, $row) = @_; @@ -278,3 +387,129 @@ is_deeply scalar $box->Select($tuple[0], $tuple[0], {want => 'arrayref'}), [\@tu is_deeply $box->Select($tuple[0]), $hash, 'select with hashify'; } + +## Tree indexes +sub def_param1 { + my $format = 'l&l'; + return { servers => $server, + namespaces => [ { + indexes => [ { + index_name => 'primary_num1', + keys => [0], + }, { + index_name => 'secondary_str2', + keys => [1], + }, { + index_name => 'secondary_complex', + keys => [1, 2], + } ], + namespace => 26, + format => $format, + default_index => 'primary_num1', + } ]} +} + +$box = MR::SilverBox->new(def_param1); +ok $box->isa('MR::SilverBox'), 'connect'; + +my @tuple1 = (13, 'mail.ru', 123); +cleanup $tuple1[0]; + +my @tuple2 = (14, 'mail.ru', 456); +cleanup $tuple2[0]; + +ok $box->Insert(@tuple1); + +is_deeply [$box->Select([[$tuple1[0]]])], [\@tuple1], 'select by primary_num1 index'; +is_deeply [$box->Select([[$tuple1[1]]], { use_index => 'secondary_str2' })], [\@tuple1], 'select by secondary_str2 index'; +is_deeply [$box->Select([[$tuple1[1], $tuple1[2]]], { use_index => 'secondary_complex' })], [\@tuple1], 'select by secondary_complex index'; +is_deeply [$box->Select([[$tuple1[1]]], { use_index => 'secondary_complex' })], [\@tuple1], 'select by secondary_complex index, partial key'; + +ok $box->Insert(@tuple2); + +is_deeply [$box->Select([[$tuple2[0]]])], [\@tuple2], 'select by primary_num1 index'; +is_deeply [$box->Select([[$tuple1[1]]], { use_index => 'secondary_str2', limit => 2, offset => 0 })], [\@tuple1, \@tuple2], 'select by secondary_str2 index'; +is_deeply [$box->Select([[$tuple2[1], $tuple2[2]]], { use_index => 'secondary_complex' })], [\@tuple2], 'select by secondary_complex index'; + +## Check index constrains +sub def_param_bad { + my $format = 'l&&'; + return { servers => $server, + namespaces => [ { + indexes => [ { + index_name => 'primary_num1', + keys => [0], + }, { + index_name => 'secondary_str2', + keys => [1], + }, { + index_name => 'secondary_complex', + keys => [1, 2], + } ], + namespace => 26, + format => $format, + default_index => 'primary_num1', + } ]} +} + +$box = MR::SilverBox->new(def_param_bad); +ok $box->isa('MR::SilverBox'), 'connect'; + +my @tuple_bad = (13, 'mail.ru', '123'); +cleanup $tuple_bad[0]; +throws_ok sub { $box->Insert(@tuple_bad) }, qr/Illegal parametrs/, "index_constains/bad_field_type"; + + +## Check unique tree index +sub def_param_unique { + my $format = 'l&&&'; + return { servers => $server, + namespaces => [ { + indexes => [ { + index_name => 'id', + keys => [0], + }, { + index_name => 'email', + keys => [1], + }, { + index_name => 'firstname', + keys => [2], + }, { + index_name => 'lastname', + keys => [3], + } , { + index_name => 'fullname', + keys => [2, 3] + } ], + namespace => 27, + format => $format, + default_index => 'id', + } ]} +} + +$box = MR::SilverBox->new(def_param_unique); +ok $box->isa('MR::SilverBox'), 'connect'; + +my $tuples = [ [1, 'rtokarev@corp.mail.ru', 'Roman', 'Tokarev'], + [2, 'vostrikov@corp.mail.ru', 'Yuri', 'Vostrikov'], + [3, 'aleinikov@corp.mail.ru', 'Roman', 'Aleinikov'], + [4, 'roman.s.tokarev@gmail.com', 'Roman', 'Tokarev'], + [5, 'vostrikov@corp.mail.ru', 'delamon', 'delamon'] ]; + +foreach my $tuple (@$tuples) { + cleanup $tuple->[0]; +} + +foreach my $tuple (@$tuples) { + if ($tuple == $tuples->[-1] || $tuple == $tuples->[-2]) { + throws_ok sub { $box->Insert(@$tuple) }, qr/Index violation/, "unique_tree_index/insert \'$tuple->[0]\'"; + } else { + ok $box->Insert(@$tuple), "unique_tree_index/insert \'$tuple->[0]\'"; + } +} + +my @res = $box->Select([map $_->[0], @$tuples], { limit => 100 }); +foreach my $r (@res) { + ok sub { return $r != $tuples->[-1] && $r != $tuples->[-2] }; +} + diff --git a/scripts/config_def.mk b/scripts/config_def.mk index 0d4585ed3afeedd116259e77f84f82b0de4b8488..5de0c0512ffea4b4e1ca76cafb586ad9a3fa78cf 100644 --- a/scripts/config_def.mk +++ b/scripts/config_def.mk @@ -26,7 +26,7 @@ endif ifeq ($(OS), Linux) CFLAGS += -DLinux -D_FILE_OFFSET_BITS=64 -DEV_USE_INOTIFY -D_GNU_SOURCE - ifeq ($(DEBUG), 2) + ifeq ($(DEBUG), 1) CFLAGS += -DHAVE_VALGRIND endif endif @@ -35,14 +35,8 @@ ifeq (,$(filter -g%,$(CFLAGS))) CFLAGS += -g3 -ggdb endif -ifdef DEBUG - CFLAGS += -DDEBUG -fno-omit-frame-pointer -else - CFLAGS += -DNDEBUG -endif - ifeq (,$(filter -O%,$(CFLAGS))) - ifeq ($(DEBUG), 2) + ifeq ($(DEBUG), 1) CFLAGS += -O0 else CFLAGS += -O2 diff --git a/scripts/custom_types.hs b/scripts/custom_types.hs new file mode 100644 index 0000000000000000000000000000000000000000..83b454d25490834940330ffb930e45d47d285443 --- /dev/null +++ b/scripts/custom_types.hs @@ -0,0 +1,34 @@ +module Main where + +import System +import qualified Data.Map as M +import Data.Maybe +import Language.C +import Language.C.System.GCC +import Language.C.Analysis.AstAnalysis +import Language.C.Analysis.SemRep +import Language.C.Analysis.TravMonad + +main = do + args <- getArgs + parseFile (head args) (tail args) >>= printGlobalTypes + +parseFile :: FilePath -> [String] -> IO CTranslUnit +parseFile input_file cOpts = + do parse_result <- parseCFile (newGCC "gcc") Nothing cOpts input_file + case parse_result of + Left parse_err -> error (show parse_err) + Right ast -> return ast + +printGlobalTypes transUnit = + case runTrav_ (analyseAST transUnit) of + Left err -> error (show err) + Right (globals, _) -> mapM_ print $ globalTypeNames globals + +globalTypeNames g = + map identToString $ typeNames ++ sueNames + where + typeNames = M.keys (gTypeDefs g) + sueNames = mapMaybe named (M.keys $ gTags g) + named (NamedRef x) = Just x + named _ = Nothing diff --git a/scripts/indent b/scripts/indent index 840d112fc7236db75e0804969f84ded15272c7e4..85548597d38246672b46973ee83449e36f90b1bf 100755 --- a/scripts/indent +++ b/scripts/indent @@ -1,5 +1,25 @@ #!/bin/sh INDENT=$(which gindent 2>/dev/null || which indent 2>/dev/null) [ -z $INDENT ] && echo indent binary not found && exit 1 -PARAM="-npro -kr -i8 -ts8 -sob -l100 -ss -ncs -cp1" -$INDENT $PARAM $@ + +for f in $@ +do + PARAM="-npro -kr -i8 -ts8 -ci8 -sob -l100 -ss -ncs -cp1 + -T bool" + + case "$f" in + *.c) + PARAM="$PARAM -psl" + ;; + *.h) + ;; + *) + echo "Unknown file type: $f" + exit 1; + ;; + esac + + PARAM="$PARAM "$(scripts/custom_types $f -I. -Iinclude -DCORO_ASM -DSTORAGE -DTARANTOOL_CONFIG='<cfg/tarantool_silverbox_cfg.h>' | grep -v '^"_' | sed 's/"//g; s/^/-T /') + + $INDENT $PARAM $f +done diff --git a/scripts/rules.mk b/scripts/rules.mk new file mode 100644 index 0000000000000000000000000000000000000000..459c3d55ee83f23eb787ce64120c833541e0d89d --- /dev/null +++ b/scripts/rules.mk @@ -0,0 +1,108 @@ +ifeq (,$(module)) +all: + @echo "Valid targets are:" + @echo " _release_box/tarantool_silverbox" + @echo " _release_feeder/tarantool_feeder" + @echo " _debug_box/tarantool_silverbox" + @echo " _debug_feeder/tarantool_feeder" + @echo " clean" +else +all: tarantool_$(module) +endif + + +-include $(SRCDIR)/config.mk $(SRCDIR)/scripts/config.mk +include $(SRCDIR)/scripts/config_def.mk + +ifneq (,$(findstring _release,$(OBJDIR))) + CFLAGS += -DNDEBUG +else ifneq (,$(findstring _debug,$(OBJDIR))) + DEBUG=1 + CFLAGS += -DDEBUG -fno-omit-frame-pointer +else ifneq (,$(findstring _test,$(OBJDIR))) + CFLAGS += --coverage -DCOVERAGE -DNDEBUG +else ifneq (,$(findstring _coverage,$(OBJDIR))) + CFLAGS += --coverage -DCOVERAGE +endif + +# build dir includes going first +ifneq (,$(OBJDIR)) +CFLAGS += -I$(SRCDIR)/$(OBJDIR) -I$(SRCDIR)/$(OBJDIR)/include +endif +CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/include +LIBS += -lm + +subdirs += third_party +ifneq (,$(module)) + subdirs += mod/$(module) +endif +subdirs += cfg core +include $(foreach dir,$(subdirs),$(SRCDIR)/$(dir)/Makefile) + +tarantool_$(module): $(obj) + $(CC) $(CFLAGS) $(LDFLAGS) $(LIBS) $^ -o $@ + +ifdef I +$(info * build with $(filter -D%,$(CFLAGS))) +endif + +# makefile change will force rebuild +$(obj): $(wildcard ../*.mk) $(wildcard ../scripts/*.mk) +$(obj): $(foreach dir,$(subdirs),$(SRCDIR)/$(dir)/Makefile) +$(obj): Makefile + +dep = $(patsubst %.o,%.d,$(obj)) $(patsubst %.o,%.pd,$(obj)) +-include $(dep) + +ifneq (,$(OBJDIR)) +%.o: %.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -MD -c $< -o $@ + @sed -n -f $(SRCDIR)/scripts/slurp.sed \ + -f $(SRCDIR)/scripts/fixdep.sed \ + -e 's!$(SRCDIR)/!!; p' \ + < $(@:.o=.d) > $(@:.o=.pd) +else +%.o: %.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -MD -c $< -o $@ +endif + +# code gen targets +.PRECIOUS: %.h %.c %.dot +ifeq ($(HAVE_RAGEL),1) +%.c: %.rl + @mkdir -p $(dir $@) + $(RAGEL) -G2 $< -o $@ + +%.dot: %.rl + @mkdir -p $(dir $@) + $(RAGEL) -p -V $< -o $(basename $@).dot + +%.png: %.dot + @mkdir -p $(dir $@) + $(DOT) -Tpng $< -o $(basename $@).png +endif +ifeq ($(HAVE_CONFETTI),1) +%.cfg: %.cfg_tmpl + @mkdir -p $(dir $@) + $(CONFETTI) -i $< -n tarantool_cfg -f $@ + +%.h: %.cfg_tmpl + @mkdir -p $(dir $@) + $(CONFETTI) -i $< -n tarantool_cfg -h $@ + +%.c: %.cfg_tmpl + @mkdir -p $(dir $@) + $(CONFETTI) -i $< -n tarantool_cfg -c $@ +endif + +ifeq ($(HAVE_GIT),1) +tarantool_version.h: FORCE + @echo -n "const char tarantool_version_string[] = " > $@_ + @git show HEAD | sed 's/commit \(.*\)/\"\1/;q' | tr -d \\n >> $@_ + @git diff --quiet || (echo -n ' AND'; git diff --shortstat) | tr -d \\n >> $@_ + @echo '";' >> $@_ + @diff -q $@ $@_ 2>/dev/null >/dev/null || ($(ECHO) " GEN " $(notdir $@); cp $@_ $@) +FORCE: +endif diff --git a/scripts/run_test.sh b/scripts/run_test.sh index 743b673218763bb3618dedd51fd518a21fab6ec0..ce6abef662c744e6f6d43c99f69b8f81009efa96 100755 --- a/scripts/run_test.sh +++ b/scripts/run_test.sh @@ -67,8 +67,8 @@ write_config() { local admin_port=${3:?} cat <<-EOF > $test_dir/$file_name.cfg - log_level = 2 - slab_alloc_arena = 1 + log_level = 4 + slab_alloc_arena = 0.1 work_dir = "$test_dir" snap_dir = "storage/snap" @@ -89,46 +89,141 @@ write_config() { wal_feeder_port = 24444 namespace[0].enabled = 1 - namespace[0].index[0].type = "NUM" - namespace[0].index[0].key_position = 0 - namespace[0].index[1].type = "STR" - namespace[0].index[1].key_position = 1 + namespace[0].index[0].type = "HASH" + namespace[0].index[0].unique = 1 + namespace[0].index[0].key_field[0].fieldno = 0 + namespace[0].index[0].key_field[0].type = "NUM" + namespace[0].index[1].type = "HASH" + namespace[0].index[1].unique = 1 + namespace[0].index[1].key_field[0].fieldno = 1 + namespace[0].index[1].key_field[0].type = "STR" namespace[3].enabled = 1 - namespace[3].index[0].type = "NUM" - namespace[3].index[0].key_position = 0 + namespace[3].index[0].type = "HASH" + namespace[3].index[0].unique = 1 + namespace[3].index[0].key_field[0].fieldno = 0 + namespace[3].index[0].key_field[0].type = "NUM" namespace[5].enabled = 1 - namespace[5].index[0].type = "NUM" - namespace[5].index[0].key_position = 0 + namespace[5].index[0].type = "HASH" + namespace[5].index[0].unique = 1 + namespace[5].index[0].key_field[0].fieldno = 0 + namespace[5].index[0].key_field[0].type = "NUM" namespace[11].enabled = 1 - namespace[11].index[0].type = "NUM" - namespace[11].index[0].key_position = 0 + namespace[11].index[0].type = "HASH" + namespace[11].index[0].unique = 1 + namespace[11].index[0].key_field[0].fieldno = 0 + namespace[11].index[0].key_field[0].type = "NUM" namespace[19].enabled = 1 - namespace[19].index[0].type = "STR" - namespace[19].index[0].key_position = 0 + namespace[19].index[0].type = "HASH" + namespace[19].index[0].unique = 1 + namespace[19].index[0].key_field[0].fieldno = 0 + namespace[19].index[0].key_field[0].type = "STR" namespace[22].enabled = 1 - namespace[22].index[0].type = NUM - namespace[22].index[0].key_position = 0 + namespace[22].index[0].type = "HASH" + namespace[22].index[0].unique = 1 + namespace[22].index[0].key_field[0].fieldno = 0 + namespace[22].index[0].key_field[0].type = "NUM" namespace[23].enabled = 1 - namespace[23].index[0].type = NUM - namespace[23].index[0].key_position = 0 + namespace[23].index[0].type = "HASH" + namespace[23].index[0].unique = 1 + namespace[23].index[0].key_field[0].fieldno = 0 + namespace[23].index[0].key_field[0].type = "NUM" namespace[24].enabled = 1 - # namespace[24].dual_to = 25 - namespace[24].index[0].type = "NUM" - namespace[24].index[0].key_position = 0 - namespace[24].index[1].type = "STR" - namespace[24].index[1].key_position = 1 + namespace[24].index[0].type = "HASH" + namespace[24].index[0].unique = 1 + namespace[24].index[0].key_field[0].fieldno = 0 + namespace[24].index[0].key_field[0].type = "NUM" + namespace[24].index[1].type = "HASH" + namespace[24].index[1].unique = 1 + namespace[24].index[1].key_field[0].fieldno = 1 + namespace[24].index[1].key_field[0].type = "STR" #namespace[25].enabled = 0 #namespace[25].dual_to = 24 - #namespace[25].index[0].type = "STR" - #namespace[25].index[0].key_position = 1 + #namespace[25].index[0].type = "HASH" + #namespace[25].index[0].key_field[0].fieldno = 1 + #namespace[25].index[0].key_field[0].type = "STR" + + namespace[26].enabled = 1 + namespace[26].index[0].type = "HASH" + namespace[26].index[0].unique = 1 + namespace[26].index[0].key_field[0].fieldno = 0 + namespace[26].index[0].key_field[0].type = "NUM" + namespace[26].index[1].type = "TREE" + namespace[26].index[1].unique = 0 + namespace[26].index[1].key_field[0].fieldno = 1 + namespace[26].index[1].key_field[0].type = "STR" + namespace[26].index[2].type = "TREE" + namespace[26].index[2].unique = 1 + namespace[26].index[2].key_field[0].fieldno = 1 + namespace[26].index[2].key_field[0].type = "STR" + namespace[26].index[2].key_field[1].fieldno = 2 + namespace[26].index[2].key_field[1].type = "NUM" + + namespace[27] = { + enabled = 1 + + # Email + index[0] = { + type = "HASH" + unique = 1 + + key_field[0] = { + type = "NUM" + fieldno = 0 + } + } + index[1] = { + type = "TREE" + unique = 1 + + key_field[0] = { + type = "STR" + fieldno = 1 + } + } + # FirstName + index[2] = { + type = "TREE" + unique = 0 + + key_field[0] = { + type = "STR" + fieldno = 2 + } + } + # LastName + index[3] = { + type = "TREE" + unique = 0 + + key_field[0] = { + type = "STR" + fieldno = 3 + } + } + # FullName + index[4] = { + type = "TREE" + unique = 1 + + key_field[0] = { + type = "STR" + fieldno = 2 + } + key_field[1] = { + type = "STR" + fieldno = 3 + } + } + } + EOF } diff --git a/third_party/confetti/prscfg.c b/third_party/confetti/prscfg.c index 43cefce17bc2ede47d8e7280ea909f000d1d1672..98358885e3d38a5cc88ad13da4c0b5e204034cc5 100644 --- a/third_party/confetti/prscfg.c +++ b/third_party/confetti/prscfg.c @@ -62,7 +62,8 @@ static int prscfgGetLineNo(prscfg_yyscan_t yyscanner); KEY_P = 258, NULL_P = 259, STRING_P = 260, - NUMBER_P = 261 + NUMBER_P = 261, + PATH_P = 262 }; #endif /* Tokens. */ @@ -70,6 +71,7 @@ static int prscfgGetLineNo(prscfg_yyscan_t yyscanner); #define NULL_P 259 #define STRING_P 260 #define NUMBER_P 261 +#define PATH_P 262 @@ -89,7 +91,7 @@ typedef union YYSTYPE /* Line 1676 of yacc.c */ -#line 73 "y.tab.h" +#line 75 "y.tab.h" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ @@ -275,7 +277,8 @@ static OptDef *output; KEY_P = 258, NULL_P = 259, STRING_P = 260, - NUMBER_P = 261 + NUMBER_P = 261, + PATH_P = 262 }; #endif /* Tokens. */ @@ -283,6 +286,7 @@ static OptDef *output; #define NULL_P 259 #define STRING_P 260 #define NUMBER_P 261 +#define PATH_P 262 @@ -302,7 +306,7 @@ typedef union YYSTYPE /* Line 214 of yacc.c */ -#line 205 "y.tab.c" +#line 207 "y.tab.c" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ @@ -314,7 +318,7 @@ typedef union YYSTYPE /* Line 264 of yacc.c */ -#line 217 "y.tab.c" +#line 219 "y.tab.c" #ifdef short # undef short @@ -527,22 +531,22 @@ union yyalloc #endif /* YYFINAL -- State number of the termination state. */ -#define YYFINAL 9 +#define YYFINAL 10 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 36 +#define YYLAST 54 /* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 14 +#define YYNTOKENS 15 /* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 8 +#define YYNNTS 10 /* YYNRULES -- Number of rules. */ -#define YYNRULES 18 +#define YYNRULES 24 /* YYNRULES -- Number of states. */ -#define YYNSTATES 34 +#define YYNSTATES 47 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 -#define YYMAXUTOK 261 +#define YYMAXUTOK 262 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) @@ -554,15 +558,15 @@ static const yytype_uint8 yytranslate[] = 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 13, 2, 9, 2, 2, 2, + 2, 2, 2, 2, 14, 2, 10, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 10, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 11, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 7, 2, 8, 2, 2, 2, 2, 2, 2, + 2, 8, 2, 9, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 11, 2, 12, 2, 2, 2, 2, + 2, 2, 2, 12, 2, 13, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -576,7 +580,7 @@ static const yytype_uint8 yytranslate[] = 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, - 5, 6 + 5, 6, 7 }; #if YYDEBUG @@ -585,26 +589,31 @@ static const yytype_uint8 yytranslate[] = static const yytype_uint8 yyprhs[] = { 0, 0, 3, 5, 7, 9, 11, 16, 18, 22, - 24, 27, 31, 35, 39, 43, 49, 55, 59 + 27, 31, 33, 37, 41, 45, 49, 53, 57, 64, + 71, 78, 80, 81, 86 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int8 yyrhs[] = { - 15, 0, -1, 19, -1, 3, -1, 4, -1, 16, - -1, 16, 7, 6, 8, -1, 16, -1, 17, 9, - 18, -1, 20, -1, 19, 20, -1, 18, 10, 6, - -1, 18, 10, 5, -1, 18, 10, 3, -1, 18, - 10, 4, -1, 18, 10, 11, 19, 12, -1, 18, - 10, 7, 21, 8, -1, 11, 19, 12, -1, 21, - 13, 11, 19, 12, -1 + 16, 0, -1, 21, -1, 3, -1, 4, -1, 17, + -1, 17, 8, 6, 9, -1, 17, -1, 18, 10, + 19, -1, 17, 8, 6, 9, -1, 18, 10, 20, + -1, 22, -1, 21, 23, 22, -1, 19, 11, 6, + -1, 19, 11, 5, -1, 19, 11, 7, -1, 19, + 11, 3, -1, 19, 11, 4, -1, 19, 11, 12, + 21, 23, 13, -1, 19, 11, 8, 24, 23, 9, + -1, 20, 11, 12, 21, 23, 13, -1, 14, -1, + -1, 12, 21, 23, 13, -1, 24, 23, 12, 21, + 23, 13, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint8 yyrline[] = { - 0, 91, 91, 95, 96, 100, 101, 110, 111, 116, - 117, 121, 122, 123, 124, 125, 126, 130, 141 + 0, 92, 92, 96, 97, 101, 102, 111, 112, 116, + 122, 126, 127, 131, 132, 133, 134, 135, 136, 137, + 138, 142, 143, 147, 158 }; #endif @@ -614,9 +623,9 @@ static const yytype_uint8 yyrline[] = static const char *const yytname[] = { "$end", "error", "$undefined", "KEY_P", "NULL_P", "STRING_P", - "NUMBER_P", "'['", "']'", "'.'", "'='", "'{'", "'}'", "','", "$accept", - "cfg", "identifier", "elem_identifier", "keyname", "param_list", "param", - "struct_list", 0 + "NUMBER_P", "PATH_P", "'['", "']'", "'.'", "'='", "'{'", "'}'", "','", + "$accept", "cfg", "identifier", "elem_identifier", "keyname", + "array_keyname", "param_list", "param", "comma_opt", "struct_list", 0 }; #endif @@ -625,23 +634,25 @@ static const char *const yytname[] = token YYLEX-NUM. */ static const yytype_uint16 yytoknum[] = { - 0, 256, 257, 258, 259, 260, 261, 91, 93, 46, - 61, 123, 125, 44 + 0, 256, 257, 258, 259, 260, 261, 262, 91, 93, + 46, 61, 123, 125, 44 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { - 0, 14, 15, 16, 16, 17, 17, 18, 18, 19, - 19, 20, 20, 20, 20, 20, 20, 21, 21 + 0, 15, 16, 17, 17, 18, 18, 19, 19, 20, + 20, 21, 21, 22, 22, 22, 22, 22, 22, 22, + 22, 23, 23, 24, 24 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { - 0, 2, 1, 1, 1, 1, 4, 1, 3, 1, - 2, 3, 3, 3, 3, 5, 5, 3, 5 + 0, 2, 1, 1, 1, 1, 4, 1, 3, 4, + 3, 1, 3, 3, 3, 3, 3, 3, 6, 6, + 6, 1, 0, 4, 6 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state @@ -649,64 +660,71 @@ static const yytype_uint8 yyr2[] = means the default is an error. */ static const yytype_uint8 yydefact[] = { - 0, 3, 4, 0, 5, 0, 0, 2, 9, 1, - 0, 0, 0, 10, 0, 8, 13, 14, 12, 11, - 0, 0, 6, 0, 0, 0, 0, 16, 0, 15, - 17, 0, 0, 18 + 0, 3, 4, 0, 5, 0, 0, 0, 22, 11, + 1, 0, 0, 0, 0, 21, 0, 0, 8, 10, + 16, 17, 14, 13, 15, 0, 0, 0, 12, 6, + 0, 22, 22, 22, 22, 0, 0, 0, 0, 19, + 0, 18, 20, 23, 22, 0, 24 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int8 yydefgoto[] = { - -1, 3, 4, 5, 6, 7, 8, 24 + -1, 3, 4, 5, 6, 7, 8, 9, 16, 31 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ -#define YYPACT_NINF -21 +#define YYPACT_NINF -26 static const yytype_int8 yypact[] = { - 29, -21, -21, 2, 20, 7, 0, 29, -21, -21, - 6, 29, 17, -21, 21, -21, -21, -21, -21, -21, - 23, 29, -21, 29, 18, 1, 3, -21, 24, -21, - -21, 29, 5, -21 + 29, -26, -26, 6, -4, 4, 17, 24, 3, -26, + -26, 31, 29, 38, 26, -26, 29, 30, -26, -26, + -26, -26, -26, -26, -26, 35, 29, 29, -26, 37, + 29, 39, 39, 39, 39, 7, 5, 21, 23, -26, + 29, -26, -26, -26, 39, 27, -26 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int8 yypgoto[] = { - -21, -21, -21, -21, 25, -20, -7, -21 + -26, -26, -26, -26, 40, 42, -25, -16, -21, -26 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If zero, do what YYDEFACT says. If YYTABLE_NINF, syntax error. */ -#define YYTABLE_NINF -8 +#define YYTABLE_NINF -10 static const yytype_int8 yytable[] = { - 13, 25, 9, 26, 1, 2, 1, 2, 1, 2, - 12, 32, 14, 29, 0, 30, 11, 33, 13, 13, - 16, 17, 18, 19, 20, 13, 27, 10, 21, 22, - -7, 28, 1, 2, 23, 31, 15 + 28, 32, 33, -2, 11, 34, 10, -7, 1, 2, + 35, 36, 37, 38, 12, 44, 39, 15, 41, 40, + 28, 28, 28, 45, 1, 2, 1, 2, 13, 28, + 1, 2, 1, 2, 42, 14, 43, 17, 27, 29, + 46, 20, 21, 22, 23, 24, 25, 30, -9, 0, + 26, 0, 18, 15, 19 }; static const yytype_int8 yycheck[] = { - 7, 21, 0, 23, 3, 4, 3, 4, 3, 4, - 10, 31, 6, 12, -1, 12, 9, 12, 25, 26, - 3, 4, 5, 6, 7, 32, 8, 7, 11, 8, - 10, 13, 3, 4, 11, 11, 11 + 16, 26, 27, 0, 8, 30, 0, 11, 3, 4, + 31, 32, 33, 34, 10, 40, 9, 14, 13, 12, + 36, 37, 38, 44, 3, 4, 3, 4, 11, 45, + 3, 4, 3, 4, 13, 11, 13, 6, 12, 9, + 13, 3, 4, 5, 6, 7, 8, 12, 11, -1, + 12, -1, 12, 14, 12 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { - 0, 3, 4, 15, 16, 17, 18, 19, 20, 0, - 7, 9, 10, 20, 6, 18, 3, 4, 5, 6, - 7, 11, 8, 11, 21, 19, 19, 8, 13, 12, - 12, 11, 19, 12 + 0, 3, 4, 16, 17, 18, 19, 20, 21, 22, + 0, 8, 10, 11, 11, 14, 23, 6, 19, 20, + 3, 4, 5, 6, 7, 8, 12, 12, 22, 9, + 12, 24, 21, 21, 21, 23, 23, 23, 23, 9, + 12, 13, 13, 13, 21, 23, 13 }; #define yyerrok (yyerrstatus = 0) @@ -1525,35 +1543,35 @@ YYSTYPE yylval; case 2: /* Line 1455 of yacc.c */ -#line 91 "prscfg.y" +#line 92 "prscfg.y" { output = (yyval.node) = (yyvsp[(1) - (1)].node); } break; case 3: /* Line 1455 of yacc.c */ -#line 95 "prscfg.y" +#line 96 "prscfg.y" { MakeAtom((yyval.atom), (yyvsp[(1) - (1)].str)); } break; case 4: /* Line 1455 of yacc.c */ -#line 96 "prscfg.y" +#line 97 "prscfg.y" { MakeAtom((yyval.atom), (yyvsp[(1) - (1)].str)); } break; case 5: /* Line 1455 of yacc.c */ -#line 100 "prscfg.y" +#line 101 "prscfg.y" { (yyval.atom) = (yyvsp[(1) - (1)].atom); } break; case 6: /* Line 1455 of yacc.c */ -#line 101 "prscfg.y" +#line 102 "prscfg.y" { (yyval.atom) = (yyvsp[(1) - (4)].atom); (yyval.atom)->index = atoi((yyvsp[(3) - (4)].str)); @@ -1565,14 +1583,14 @@ YYSTYPE yylval; case 7: /* Line 1455 of yacc.c */ -#line 110 "prscfg.y" +#line 111 "prscfg.y" { (yyval.atom) = (yyvsp[(1) - (1)].atom); } break; case 8: /* Line 1455 of yacc.c */ -#line 111 "prscfg.y" +#line 112 "prscfg.y" { MakeList((yyval.atom), (yyvsp[(1) - (3)].atom), (yyvsp[(3) - (3)].atom)); } break; @@ -1580,97 +1598,144 @@ YYSTYPE yylval; /* Line 1455 of yacc.c */ #line 116 "prscfg.y" - { (yyval.node) = (yyvsp[(1) - (1)].node); } + { + (yyval.atom) = (yyvsp[(1) - (4)].atom); + (yyval.atom)->index = atoi((yyvsp[(3) - (4)].str)); + /* XXX check !*/ + free((yyvsp[(3) - (4)].str)); + } break; case 10: /* Line 1455 of yacc.c */ -#line 117 "prscfg.y" - { MakeList((yyval.node), (yyvsp[(2) - (2)].node), (yyvsp[(1) - (2)].node)); /* plainOptDef will revert the list */ } +#line 122 "prscfg.y" + { MakeList((yyval.atom), (yyvsp[(1) - (3)].atom), (yyvsp[(3) - (3)].atom)); } break; case 11: /* Line 1455 of yacc.c */ -#line 121 "prscfg.y" - { MakeScalarParam((yyval.node), number, (yyvsp[(1) - (3)].atom), (yyvsp[(3) - (3)].str)); } +#line 126 "prscfg.y" + { (yyval.node) = (yyvsp[(1) - (1)].node); } break; case 12: /* Line 1455 of yacc.c */ -#line 122 "prscfg.y" - { MakeScalarParam((yyval.node), string, (yyvsp[(1) - (3)].atom), (yyvsp[(3) - (3)].str)); } +#line 127 "prscfg.y" + { MakeList((yyval.node), (yyvsp[(3) - (3)].node), (yyvsp[(1) - (3)].node)); /* plainOptDef will revert the list */ } break; case 13: /* Line 1455 of yacc.c */ -#line 123 "prscfg.y" - { MakeScalarParam((yyval.node), string, (yyvsp[(1) - (3)].atom), (yyvsp[(3) - (3)].str)); } +#line 131 "prscfg.y" + { MakeScalarParam((yyval.node), number, (yyvsp[(1) - (3)].atom), (yyvsp[(3) - (3)].str)); } break; case 14: /* Line 1455 of yacc.c */ -#line 124 "prscfg.y" - { MakeScalarParam((yyval.node), string, (yyvsp[(1) - (3)].atom), NULL); free((yyvsp[(3) - (3)].str)); } +#line 132 "prscfg.y" + { MakeScalarParam((yyval.node), string, (yyvsp[(1) - (3)].atom), (yyvsp[(3) - (3)].str)); } break; case 15: /* Line 1455 of yacc.c */ -#line 125 "prscfg.y" - { MakeScalarParam((yyval.node), struct, (yyvsp[(1) - (5)].atom), (yyvsp[(4) - (5)].node)); SetParent( (yyval.node), (yyvsp[(4) - (5)].node) ); } +#line 133 "prscfg.y" + { MakeScalarParam((yyval.node), string, (yyvsp[(1) - (3)].atom), (yyvsp[(3) - (3)].str)); } break; case 16: /* Line 1455 of yacc.c */ -#line 126 "prscfg.y" - { (yyvsp[(4) - (5)].node)->name = (yyvsp[(1) - (5)].atom); (yyval.node) = (yyvsp[(4) - (5)].node); } +#line 134 "prscfg.y" + { MakeScalarParam((yyval.node), string, (yyvsp[(1) - (3)].atom), (yyvsp[(3) - (3)].str)); } break; case 17: /* Line 1455 of yacc.c */ -#line 130 "prscfg.y" +#line 135 "prscfg.y" + { MakeScalarParam((yyval.node), string, (yyvsp[(1) - (3)].atom), NULL); free((yyvsp[(3) - (3)].str)); } + break; + + case 18: + +/* Line 1455 of yacc.c */ +#line 136 "prscfg.y" + { MakeScalarParam((yyval.node), struct, (yyvsp[(1) - (6)].atom), (yyvsp[(4) - (6)].node)); SetParent( (yyval.node), (yyvsp[(4) - (6)].node) ); } + break; + + case 19: + +/* Line 1455 of yacc.c */ +#line 137 "prscfg.y" + { (yyvsp[(4) - (6)].node)->name = (yyvsp[(1) - (6)].atom); (yyval.node) = (yyvsp[(4) - (6)].node); } + break; + + case 20: + +/* Line 1455 of yacc.c */ +#line 138 "prscfg.y" + { MakeScalarParam((yyval.node), struct, (yyvsp[(1) - (6)].atom), (yyvsp[(4) - (6)].node)); SetParent( (yyval.node), (yyvsp[(4) - (6)].node) ); } + break; + + case 21: + +/* Line 1455 of yacc.c */ +#line 142 "prscfg.y" + { (yyval.str)=NULL; } + break; + + case 22: + +/* Line 1455 of yacc.c */ +#line 143 "prscfg.y" + { (yyval.str)=NULL; } + break; + + case 23: + +/* Line 1455 of yacc.c */ +#line 147 "prscfg.y" { OptDef *str; NameAtom *idx; MakeAtom(idx, NULL); - MakeScalarParam(str, struct, idx, (yyvsp[(2) - (3)].node)); - SetParent( str, (yyvsp[(2) - (3)].node) ); + MakeScalarParam(str, struct, idx, (yyvsp[(2) - (4)].node)); + SetParent( str, (yyvsp[(2) - (4)].node) ); SetIndex( str, 0 ); MakeScalarParam((yyval.node), array, NULL, str); SetParent( (yyval.node), str ); } break; - case 18: + case 24: /* Line 1455 of yacc.c */ -#line 141 "prscfg.y" +#line 158 "prscfg.y" { OptDef *str; NameAtom *idx; MakeAtom(idx, NULL); - MakeScalarParam(str, struct, idx, (yyvsp[(4) - (5)].node)); - SetParent(str, (yyvsp[(4) - (5)].node)); - SetIndex(str, (yyvsp[(1) - (5)].node)->paramValue.arrayval->name->index + 1); - MakeList((yyvsp[(1) - (5)].node)->paramValue.arrayval, str, (yyvsp[(1) - (5)].node)->paramValue.arrayval); - SetParent((yyvsp[(1) - (5)].node), str); - (yyval.node) = (yyvsp[(1) - (5)].node); + MakeScalarParam(str, struct, idx, (yyvsp[(4) - (6)].node)); + SetParent(str, (yyvsp[(4) - (6)].node)); + SetIndex(str, (yyvsp[(1) - (6)].node)->paramValue.arrayval->name->index + 1); + MakeList((yyvsp[(1) - (6)].node)->paramValue.arrayval, str, (yyvsp[(1) - (6)].node)->paramValue.arrayval); + SetParent((yyvsp[(1) - (6)].node), str); + (yyval.node) = (yyvsp[(1) - (6)].node); } break; /* Line 1455 of yacc.c */ -#line 1573 "y.tab.c" +#line 1636 "y.tab.c" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); @@ -1882,7 +1947,7 @@ YYSTYPE yylval; /* Line 1675 of yacc.c */ -#line 155 "prscfg.y" +#line 172 "prscfg.y" static int @@ -2440,8 +2505,8 @@ static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner ); *yy_cp = '\0'; \ yyg->yy_c_buf_p = yy_cp; -#define YY_NUM_RULES 26 -#define YY_END_OF_BUFFER 27 +#define YY_NUM_RULES 27 +#define YY_END_OF_BUFFER 28 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info @@ -2449,14 +2514,14 @@ struct yy_trans_info flex_int32_t yy_verify; flex_int32_t yy_nxt; }; -static yyconst flex_int16_t yy_accept[55] = +static yyconst flex_int16_t yy_accept[56] = { 0, - 0, 0, 0, 0, 0, 0, 0, 0, 27, 25, - 23, 24, 6, 4, 25, 5, 25, 7, 3, 3, - 15, 16, 13, 14, 21, 22, 19, 20, 19, 19, - 23, 4, 7, 2, 0, 0, 3, 3, 15, 11, - 12, 21, 19, 18, 17, 8, 0, 9, 3, 0, - 1, 0, 10, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 28, 26, + 24, 25, 7, 4, 26, 6, 26, 8, 3, 3, + 16, 17, 14, 15, 22, 23, 20, 21, 20, 20, + 24, 4, 8, 5, 2, 0, 0, 3, 3, 16, + 12, 13, 22, 20, 19, 18, 9, 0, 10, 3, + 0, 1, 0, 11, 0 } ; static yyconst flex_int32_t yy_ec[256] = @@ -2464,17 +2529,17 @@ static yyconst flex_int32_t yy_ec[256] = 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 2, 1, 5, 6, 1, 1, 1, 1, 1, - 1, 7, 8, 9, 8, 10, 11, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 1, 1, 1, - 9, 1, 1, 1, 13, 13, 13, 13, 14, 13, - 13, 13, 13, 13, 13, 15, 13, 16, 13, 13, - 13, 13, 13, 13, 17, 13, 13, 13, 13, 13, - 9, 18, 9, 1, 13, 1, 13, 13, 13, 13, - - 14, 13, 13, 13, 13, 13, 13, 15, 13, 16, - 13, 13, 13, 13, 13, 13, 17, 13, 13, 13, - 13, 13, 9, 1, 9, 1, 1, 1, 1, 1, + 1, 2, 1, 5, 6, 1, 7, 1, 1, 1, + 1, 8, 9, 10, 11, 12, 13, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 1, 1, 1, + 10, 1, 1, 1, 15, 15, 15, 15, 16, 15, + 15, 15, 15, 15, 15, 17, 15, 18, 15, 15, + 15, 15, 15, 15, 19, 15, 15, 15, 15, 15, + 10, 20, 10, 1, 15, 1, 15, 15, 15, 15, + + 16, 15, 15, 15, 15, 15, 15, 17, 15, 18, + 15, 15, 15, 15, 15, 15, 19, 15, 15, 15, + 15, 15, 10, 1, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -2491,66 +2556,74 @@ static yyconst flex_int32_t yy_ec[256] = 1, 1, 1, 1, 1 } ; -static yyconst flex_int32_t yy_meta[19] = +static yyconst flex_int32_t yy_meta[21] = { 0, - 1, 1, 2, 1, 3, 1, 1, 1, 1, 1, - 1, 4, 4, 4, 4, 4, 4, 3 + 1, 1, 2, 1, 3, 1, 4, 4, 5, 1, + 6, 4, 4, 7, 8, 8, 8, 8, 8, 9 } ; -static yyconst flex_int16_t yy_base[63] = +static yyconst flex_int16_t yy_base[67] = { 0, - 0, 0, 16, 32, 51, 50, 48, 57, 52, 102, - 18, 102, 102, 47, 36, 102, 40, 59, 0, 29, - 0, 102, 102, 42, 0, 102, 0, 102, 33, 36, - 21, 40, 0, 102, 29, 16, 0, 25, 0, 102, - 102, 0, 0, 0, 0, 15, 27, 26, 21, 18, - 0, 21, 20, 102, 73, 77, 81, 27, 85, 89, - 93, 97 + 0, 0, 18, 19, 130, 129, 22, 23, 131, 134, + 25, 134, 134, 128, 115, 134, 120, 28, 0, 108, + 0, 134, 134, 123, 0, 134, 0, 134, 112, 116, + 30, 121, 0, 114, 134, 107, 93, 0, 81, 0, + 134, 134, 0, 0, 0, 0, 27, 68, 67, 56, + 23, 0, 19, 14, 134, 44, 53, 62, 68, 71, + 79, 87, 96, 105, 110, 113 } ; -static yyconst flex_int16_t yy_def[63] = +static yyconst flex_int16_t yy_def[67] = { 0, - 54, 1, 55, 55, 56, 56, 57, 57, 54, 54, - 54, 54, 54, 54, 54, 54, 54, 54, 58, 58, - 59, 54, 54, 60, 61, 54, 62, 54, 62, 62, - 54, 54, 18, 54, 54, 54, 58, 58, 59, 54, - 54, 61, 62, 62, 62, 54, 54, 54, 58, 54, - 58, 54, 54, 0, 54, 54, 54, 54, 54, 54, - 54, 54 + 55, 1, 56, 56, 57, 57, 58, 58, 55, 55, + 55, 55, 55, 55, 55, 55, 59, 55, 60, 60, + 61, 55, 55, 62, 63, 55, 64, 55, 64, 64, + 55, 55, 18, 59, 55, 55, 65, 60, 60, 61, + 55, 55, 63, 64, 64, 64, 55, 55, 55, 60, + 66, 60, 55, 55, 0, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55 } ; -static yyconst flex_int16_t yy_nxt[121] = +static yyconst flex_int16_t yy_nxt[155] = { 0, - 10, 11, 12, 11, 13, 14, 10, 15, 16, 16, - 17, 18, 19, 19, 19, 20, 19, 10, 22, 31, - 23, 31, 31, 47, 31, 52, 46, 48, 50, 53, - 37, 53, 53, 24, 22, 51, 23, 48, 48, 49, - 46, 32, 45, 44, 41, 38, 34, 33, 32, 24, - 28, 54, 26, 26, 29, 54, 54, 54, 30, 28, - 54, 54, 54, 29, 54, 54, 54, 30, 35, 54, - 33, 54, 36, 21, 21, 21, 21, 25, 25, 25, - 25, 27, 27, 27, 27, 39, 54, 54, 39, 40, - 40, 40, 40, 42, 54, 42, 42, 43, 54, 43, - - 43, 9, 54, 54, 54, 54, 54, 54, 54, 54, - 54, 54, 54, 54, 54, 54, 54, 54, 54, 54 + 10, 11, 12, 11, 13, 14, 10, 10, 15, 16, + 15, 16, 17, 18, 19, 19, 19, 20, 19, 10, + 22, 22, 23, 23, 28, 28, 31, 54, 31, 29, + 29, 31, 54, 31, 30, 30, 54, 24, 24, 36, + 47, 33, 51, 37, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 34, 52, 34, 34, 34, 34, 38, 38, 40, + 49, 49, 40, 40, 40, 40, 40, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 43, 50, 43, 43, + + 43, 43, 43, 43, 43, 44, 49, 44, 44, 44, + 44, 44, 44, 44, 48, 48, 48, 53, 53, 53, + 47, 55, 32, 46, 45, 42, 39, 35, 33, 32, + 55, 26, 26, 9, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55 } ; -static yyconst flex_int16_t yy_chk[121] = +static yyconst flex_int16_t yy_chk[155] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 3, 11, - 3, 11, 31, 36, 31, 50, 46, 36, 46, 50, - 58, 53, 52, 3, 4, 49, 4, 48, 47, 38, - 35, 32, 30, 29, 24, 20, 17, 15, 14, 4, - 7, 9, 6, 5, 7, 0, 0, 0, 7, 8, - 0, 0, 0, 8, 0, 0, 0, 8, 18, 0, - 18, 0, 18, 55, 55, 55, 55, 56, 56, 56, - 56, 57, 57, 57, 57, 59, 0, 0, 59, 60, - 60, 60, 60, 61, 0, 61, 61, 62, 0, 62, - - 62, 54, 54, 54, 54, 54, 54, 54, 54, 54, - 54, 54, 54, 54, 54, 54, 54, 54, 54, 54 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 3, 4, 3, 4, 7, 8, 11, 54, 11, 7, + 8, 31, 53, 31, 7, 8, 51, 3, 4, 18, + 47, 18, 47, 18, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 59, 50, 59, 59, 59, 59, 60, 60, 61, + 49, 48, 61, 61, 61, 61, 61, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 63, 39, 63, 63, + + 63, 63, 63, 63, 63, 64, 37, 64, 64, 64, + 64, 64, 64, 64, 65, 65, 65, 66, 66, 66, + 36, 34, 32, 30, 29, 24, 20, 17, 15, 14, + 9, 6, 5, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55 } ; /* The intent behind this definition is that it'll catch @@ -2582,7 +2655,7 @@ static YY_BUFFER_STATE buf = NULL; -#line 504 "prscfg_scan.c" +#line 512 "prscfg_scan.c" #define INITIAL 0 #define xQUOTED 1 @@ -2824,7 +2897,7 @@ YY_DECL #line 40 "prscfg.l" -#line 746 "prscfg_scan.c" +#line 754 "prscfg_scan.c" yylval = yylval_param; @@ -2879,13 +2952,13 @@ YY_DECL while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 55 ) + if ( yy_current_state >= 56 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; ++yy_cp; } - while ( yy_current_state != 54 ); + while ( yy_current_state != 55 ); yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; @@ -2943,11 +3016,21 @@ YY_RULE_SETUP case 5: YY_RULE_SETUP #line 65 "prscfg.l" -{ return *yytext; } +{ + yylval->str = strdupn(yytext, yyleng); + if (!yylval->str) + scan_yyerror("No memory", yyextra->lineno); + return PATH_P; + } YY_BREAK case 6: YY_RULE_SETUP -#line 67 "prscfg.l" +#line 72 "prscfg.l" +{ return *yytext; } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 74 "prscfg.l" { yyextra->total = 256; yyextra->strbuf = malloc(yyextra->total); @@ -2957,16 +3040,6 @@ YY_RULE_SETUP BEGIN xQUOTED; } YY_BREAK -case 7: -YY_RULE_SETUP -#line 76 "prscfg.l" -{ - yylval->str = strdupn(yytext, yyleng); - if (!yylval->str) - scan_yyerror("No memory", yyextra->lineno); - return NUMBER_P; - } - YY_BREAK case 8: YY_RULE_SETUP #line 83 "prscfg.l" @@ -3000,22 +3073,32 @@ YY_RULE_SETUP case 11: YY_RULE_SETUP #line 104 "prscfg.l" +{ + yylval->str = strdupn(yytext, yyleng); + if (!yylval->str) + scan_yyerror("No memory", yyextra->lineno); + return NUMBER_P; + } + YY_BREAK +case 12: +YY_RULE_SETUP +#line 111 "prscfg.l" { if (addchar(yyscanner, yytext[1])) scan_yyerror("No memory", yyextra->lineno); } YY_BREAK -case 12: -/* rule 12 can match eol */ +case 13: +/* rule 13 can match eol */ YY_RULE_SETUP -#line 109 "prscfg.l" +#line 116 "prscfg.l" { yyextra->lineno++; } YY_BREAK -case 13: +case 14: YY_RULE_SETUP -#line 113 "prscfg.l" +#line 120 "prscfg.l" { yyextra->strbuf[yyextra->length] = '\0'; yylval->str = yyextra->strbuf; @@ -3024,47 +3107,47 @@ YY_RULE_SETUP return STRING_P; } YY_BREAK -case 14: +case 15: YY_RULE_SETUP -#line 121 "prscfg.l" +#line 128 "prscfg.l" { /* This is only needed for \ just before EOF */ } YY_BREAK case YY_STATE_EOF(xQUOTED): -#line 125 "prscfg.l" +#line 132 "prscfg.l" { return scan_yyerror("Unexpected end of string", yyextra->lineno); } YY_BREAK -case 15: +case 16: YY_RULE_SETUP -#line 129 "prscfg.l" +#line 136 "prscfg.l" { if (addstring(yyscanner, yytext, yyleng)) scan_yyerror("No memory", yyextra->lineno); } YY_BREAK -case 16: -/* rule 16 can match eol */ +case 17: +/* rule 17 can match eol */ YY_RULE_SETUP -#line 134 "prscfg.l" +#line 141 "prscfg.l" { if (addchar(yyscanner, yytext[0])) scan_yyerror("No memory", yyextra->lineno); yyextra->lineno++; } YY_BREAK -case 17: +case 18: YY_RULE_SETUP -#line 140 "prscfg.l" +#line 147 "prscfg.l" { yyextra->commentCounter++; } YY_BREAK -case 18: +case 19: YY_RULE_SETUP -#line 144 "prscfg.l" +#line 151 "prscfg.l" { yyextra->commentCounter--; if (yyextra->commentCounter == 0) @@ -3072,31 +3155,31 @@ YY_RULE_SETUP } YY_BREAK case YY_STATE_EOF(CCOMMENT): -#line 150 "prscfg.l" +#line 157 "prscfg.l" { return scan_yyerror("Unexpected end of string (inside comment)", yyextra->lineno); } YY_BREAK -case 19: +case 20: YY_RULE_SETUP -#line 154 "prscfg.l" +#line 161 "prscfg.l" { /* ignore */ } YY_BREAK -case 20: -/* rule 20 can match eol */ +case 21: +/* rule 21 can match eol */ YY_RULE_SETUP -#line 156 "prscfg.l" +#line 163 "prscfg.l" { yyextra->lineno++; } YY_BREAK -case 21: +case 22: YY_RULE_SETUP -#line 158 "prscfg.l" +#line 165 "prscfg.l" { /* ignore */ } YY_BREAK -case 22: -/* rule 22 can match eol */ +case 23: +/* rule 23 can match eol */ YY_RULE_SETUP -#line 160 "prscfg.l" +#line 167 "prscfg.l" { BEGIN INITIAL; yyextra->lineno++; @@ -3104,33 +3187,33 @@ YY_RULE_SETUP YY_BREAK case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(SCOMMENT): -#line 165 "prscfg.l" +#line 172 "prscfg.l" { yyterminate(); } YY_BREAK -case 23: +case 24: YY_RULE_SETUP -#line 169 "prscfg.l" +#line 176 "prscfg.l" { /* ignore */ } YY_BREAK -case 24: -/* rule 24 can match eol */ +case 25: +/* rule 25 can match eol */ YY_RULE_SETUP -#line 171 "prscfg.l" +#line 178 "prscfg.l" { yyextra->lineno++; } YY_BREAK -case 25: +case 26: YY_RULE_SETUP -#line 173 "prscfg.l" +#line 180 "prscfg.l" { return scan_yyerror("syntax error: Unknown character", yyextra->lineno); } YY_BREAK -case 26: +case 27: YY_RULE_SETUP -#line 175 "prscfg.l" +#line 182 "prscfg.l" YY_FATAL_ERROR( "flex scanner jammed" ); YY_BREAK -#line 1052 "prscfg_scan.c" +#line 1070 "prscfg_scan.c" case YY_END_OF_BUFFER: { @@ -3423,7 +3506,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 55 ) + if ( yy_current_state >= 56 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; @@ -3452,11 +3535,11 @@ static int yy_get_next_buffer (yyscan_t yyscanner) while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 55 ) + if ( yy_current_state >= 56 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; - yy_is_jam = (yy_current_state == 54); + yy_is_jam = (yy_current_state == 55); return yy_is_jam ? 0 : yy_current_state; } @@ -4254,7 +4337,7 @@ void prscfg_yyfree (void * ptr , yyscan_t yyscanner) #define YYTABLES_NAME "yytables" -#line 175 "prscfg.l" +#line 182 "prscfg.l" diff --git a/third_party/confetti/prscfg.h b/third_party/confetti/prscfg.h index 7fbc20bdaff8fdb20e04cbd35b7a353397124cfb..ceeeab4e29ef39c42b1620fc38014a62d1adc38f 100644 --- a/third_party/confetti/prscfg.h +++ b/third_party/confetti/prscfg.h @@ -45,6 +45,7 @@ typedef enum ConfettyError { CNF_WRONGRANGE, CNF_NOMEMORY, CNF_SYNTAXERROR, + CNF_NOTSET, CNF_INTERNALERROR } ConfettyError; diff --git a/third_party/qsort_arg.c b/third_party/qsort_arg.c new file mode 100644 index 0000000000000000000000000000000000000000..e627af9c8e5c48599ea6fbec6bccd13c53d48e12 --- /dev/null +++ b/third_party/qsort_arg.c @@ -0,0 +1,200 @@ +/* + * Imported from PostgreSQL sources by Teodor Sigaev <teodor@sigaev.ru>, <sigaev@corp.mail.ru> + */ + +/* + * qsort_arg.c: qsort with a passthrough "void *" argument + * + * Modifications from vanilla NetBSD source: + * Add do ... while() macro fix + * Remove __inline, _DIAGASSERTs, __P + * Remove ill-considered "swap_cnt" switch to insertion sort, + * in favor of a simple check for presorted input. + * + * CAUTION: if you change this file, see also qsort.c + * + * $PostgreSQL: pgsql/src/port/qsort_arg.c,v 1.4 2007/03/18 05:36:50 neilc Exp $ + */ + +/* $NetBSD: qsort.c,v 1.13 2003/08/07 16:43:42 agc Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <third_party/qsort_arg.h> + +#define min(a, b) (a) < (b) ? a : b + +static char *med3(char *a, char *b, char *c, + int (*cmp)(const void *a, const void *b, void *arg), void *arg); +static void swapfunc(char *, char *, size_t, int); + +/* + * Qsort routine based on J. L. Bentley and M. D. McIlroy, + * "Engineering a sort function", + * Software--Practice and Experience 23 (1993) 1249-1265. + * We have modified their original by adding a check for already-sorted input, + * which seems to be a win per discussions on pgsql-hackers around 2006-03-21. + */ +#define swapcode(TYPE, parmi, parmj, n) \ +do { \ + size_t i = (n) / sizeof (TYPE); \ + TYPE *pi = (TYPE *)(void *)(parmi); \ + TYPE *pj = (TYPE *)(void *)(parmj); \ + do { \ + TYPE t = *pi; \ + *pi++ = *pj; \ + *pj++ = t; \ + } while (--i > 0); \ +} while (0) + +#define SWAPINIT(a, es) swaptype = ((char *)(a) - (char *)0) % sizeof(long) || \ + (es) % sizeof(long) ? 2 : (es) == sizeof(long)? 0 : 1; + +static void +swapfunc(char *a, char *b, size_t n, int swaptype) +{ + if (swaptype <= 1) + swapcode(long, a, b, n); + else + swapcode(char, a, b, n); +} + +#define swap(a, b) \ + if (swaptype == 0) { \ + long t = *(long *)(void *)(a); \ + *(long *)(void *)(a) = *(long *)(void *)(b); \ + *(long *)(void *)(b) = t; \ + } else \ + swapfunc(a, b, es, swaptype) + +#define vecswap(a, b, n) if ((n) > 0) swapfunc((a), (b), (size_t)(n), swaptype) + +static char * +med3(char *a, char *b, char *c, int (*cmp)(const void *a, const void *b, void *arg), void *arg) +{ + return cmp(a, b, arg) < 0 ? + (cmp(b, c, arg) < 0 ? b : (cmp(a, c, arg) < 0 ? c : a)) + : (cmp(b, c, arg) > 0 ? b : (cmp(a, c, arg) < 0 ? a : c)); +} + +void +qsort_arg(void *a, size_t n, size_t es, int (*cmp)(const void *a, const void *b, void *arg), void *arg) +{ + char *pa, + *pb, + *pc, + *pd, + *pl, + *pm, + *pn; + int d, + r, + swaptype, + presorted; + +loop:SWAPINIT(a, es); + if (n < 7) + { + for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es) + for (pl = pm; pl > (char *) a && cmp(pl - es, pl, arg) > 0; + pl -= es) + swap(pl, pl - es); + return; + } + presorted = 1; + for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es) + { + if (cmp(pm - es, pm, arg) > 0) + { + presorted = 0; + break; + } + } + if (presorted) + return; + pm = (char *) a + (n / 2) * es; + if (n > 7) + { + pl = (char *) a; + pn = (char *) a + (n - 1) * es; + if (n > 40) + { + d = (n / 8) * es; + pl = med3(pl, pl + d, pl + 2 * d, cmp, arg); + pm = med3(pm - d, pm, pm + d, cmp, arg); + pn = med3(pn - 2 * d, pn - d, pn, cmp, arg); + } + pm = med3(pl, pm, pn, cmp, arg); + } + swap(a, pm); + pa = pb = (char *) a + es; + pc = pd = (char *) a + (n - 1) * es; + for (;;) + { + while (pb <= pc && (r = cmp(pb, a, arg)) <= 0) + { + if (r == 0) + { + swap(pa, pb); + pa += es; + } + pb += es; + } + while (pb <= pc && (r = cmp(pc, a, arg)) >= 0) + { + if (r == 0) + { + swap(pc, pd); + pd -= es; + } + pc -= es; + } + if (pb > pc) + break; + swap(pb, pc); + pb += es; + pc -= es; + } + pn = (char *) a + n * es; + r = min(pa - (char *) a, pb - pa); + vecswap(a, pb - r, r); + r = min(pd - pc, pn - pd - es); + vecswap(pb, pn - r, r); + if ((r = pb - pa) > es) + qsort_arg(a, r / es, es, cmp, arg); + if ((r = pd - pc) > es) + { + /* Iterate rather than recurse to save stack space */ + a = pn - r; + n = r / es; + goto loop; + } +/* qsort_arg(pn - r, r / es, es, cmp, arg);*/ +} diff --git a/third_party/qsort_arg.h b/third_party/qsort_arg.h new file mode 100644 index 0000000000000000000000000000000000000000..8a94e1e3b4889257d845d365cd5b9677581e268a --- /dev/null +++ b/third_party/qsort_arg.h @@ -0,0 +1,8 @@ +#ifndef QSORT_ARG_H +#define QSORT_ARG_H + +#include <sys/types.h> + +void qsort_arg(void *a, size_t n, size_t es, int (*cmp)(const void *a, const void *b, void *arg), void *arg); + +#endif diff --git a/third_party/sptree.h b/third_party/sptree.h new file mode 100644 index 0000000000000000000000000000000000000000..865b70a5367883ba6aa9d611e95f555fc559a366 --- /dev/null +++ b/third_party/sptree.h @@ -0,0 +1,558 @@ +/* + * Copyright (C) 2010 Mail.RU + * Copyright (C) 2010 Teodor Sigaev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _SPTREE_H_ +#define _SPTREE_H_ + +#include <sys/types.h> +#include <string.h> +#include <stdlib.h> +#include <math.h> + +#include <third_party/qsort_arg.h> + +#ifndef SPTREE_NODE_SELF +/* + * user could suggest pointer's storage himself + */ +typedef u_int32_t spnode_t; +#define SPNIL (0xffffffff) + +typedef struct sptree_node_pointers { + u_int32_t left; /* sizeof(spnode_t) >= sizeof(sptree_node_pointers.left) !!! */ + u_int32_t right; +} sptree_node_pointers; + +#define GET_SPNODE_LEFT(snp) ( (snp)->left ) +#define SET_SPNODE_LEFT(snp, v) (snp)->left = (v) +#define GET_SPNODE_RIGHT(snp) ( (snp)->right ) +#define SET_SPNODE_RIGHT(snp, v) (snp)->right = (v) + +#endif /* SPTREE_NODE_SELF */ + +#ifndef alpha +#define alpha ((double)0.75) +#endif +#define COUNTALPHA(size) floor(log((double)(size))/log((double)1.0/alpha)) + +#define _GET_SPNODE_LEFT(n) GET_SPNODE_LEFT( t->lrpointers + (n) ) +#define _SET_SPNODE_LEFT(n, v) SET_SPNODE_LEFT( t->lrpointers + (n), (v) ) +#define _GET_SPNODE_RIGHT(n) GET_SPNODE_RIGHT( t->lrpointers + (n) ) +#define _SET_SPNODE_RIGHT(n, v) SET_SPNODE_RIGHT( t->lrpointers + (n), (v) ) + +#define ITHELEM(t, i) ( (t)->members + (t)->elemsize * (i) ) + +/* + * makes definition of tree with methods, name should + * be unique across all definitions. + * + * Methods: + * void sptree_NAME_init(sptree_NAME *tree, size_t elemsize, void *array, + * spnode_t array_len, spnode_t array_size, + * int (*compar)(const void *, const void *, void *), void *arg) + * void* sptree_NAME_find(sptree_NAME *tree, void *key) + * void sptree_NAME_insert(sptree_NAME *tree, void *value) + * void sptree_NAME_delete(sptree_NAME *tree, void *value) + * spnode_t sptree_NAME_walk(sptree_NAME *t, void* array, spnode_t limit, spnode_t offset) + * sptree_NAME_walk_cb(sptree_NAME *t, int (*cb)(void* cb_arg, void* elem), void *cb_arg ) + * sptree_NAME_iterator* sptree_NAME_iterator_init(sptree_NAME *t) + * void sptree_NAME_iterator_init_set(sptree_NAME *t, sptree_NAME_iterator **iterator, void *start) + * void* sptree_NAME_iterator_next(sptree_NAME_iterator *i) + * void sptree_NAME_iterator_free(sptree_NAME_iterator *i) + */ + +#define SPTREE_DEF(name, realloc) \ +typedef struct sptree_##name { \ + void *members; \ + sptree_node_pointers *lrpointers; \ + \ + spnode_t nmember; \ + spnode_t ntotal; \ + \ + int (*compare)(const void *, const void *, void *); \ + void* arg; \ + size_t elemsize; \ + \ + spnode_t root; \ + spnode_t garbage_head; \ + spnode_t size; \ + spnode_t max_size; \ + spnode_t max_depth; \ +} sptree_##name; \ + \ +static spnode_t \ +sptree_##name##_mktree(sptree_##name *t, spnode_t depth, \ + spnode_t start, spnode_t end) { \ + spnode_t half = ( (end + start) >> 1 ), tmp; \ + \ + if (depth > t->max_depth) t->max_depth = depth; \ + \ + if ( half == start || \ + ( tmp = sptree_##name##_mktree(t, depth+1, start, half)) == half ) \ + _SET_SPNODE_LEFT(half, SPNIL); \ + else \ + _SET_SPNODE_LEFT(half, tmp); \ + if ( half+1 >= end || \ + ( tmp = sptree_##name##_mktree(t, depth+1, half+1, end)) == half ) \ + _SET_SPNODE_RIGHT(half, SPNIL); \ + else \ + _SET_SPNODE_RIGHT(half, tmp); \ + \ + return half; \ +} \ + \ +static inline void \ +sptree_##name##_init(sptree_##name *t, size_t elemsize, void *m, \ + spnode_t nm, spnode_t nt, \ + int (*compare)(const void *, const void *, void *), void *arg) { \ + memset(t, 0, sizeof(*t)); \ + t->members = m; \ + t->max_size = t->size = t->nmember = nm; \ + t->ntotal = (nt==0) ? nm : nt; \ + t->compare = compare; \ + t->arg = arg; \ + t->elemsize = elemsize; \ + t->garbage_head = t->root = SPNIL; \ + \ + if (t->ntotal == 0 || t->members == NULL) { /* from scratch */ \ + if (t->ntotal == 0) { \ + t->members = NULL; \ + t->ntotal = 64; \ + } \ + \ + if (t->members == NULL) \ + t->members = realloc(NULL, elemsize * t->ntotal); \ + } \ + t->lrpointers = realloc(NULL, sizeof(sptree_node_pointers) * t->ntotal); \ + \ + if (t->nmember == 1) { \ + t->root = 0; \ + _SET_SPNODE_RIGHT(0, SPNIL); \ + _SET_SPNODE_LEFT(0, SPNIL); \ + } else if (t->nmember > 1) { \ + qsort_arg(t->members, t->nmember, elemsize, t->compare, t->arg); \ + /* create tree */ \ + t->root = sptree_##name##_mktree(t, 1, 0, t->nmember); \ + } \ +} \ + \ +static inline void* \ +sptree_##name##_find(sptree_##name *t, void *k) { \ + spnode_t node = t->root; \ + while(node != SPNIL) { \ + int r = t->compare(k, ITHELEM(t, node), t->arg); \ + if (r > 0) { \ + node = _GET_SPNODE_RIGHT(node); \ + } else if (r < 0) { \ + node = _GET_SPNODE_LEFT(node); \ + } else { \ + return ITHELEM(t, node); \ + } \ + } \ + return NULL; \ +} \ + \ +static inline spnode_t \ +sptree_##name##_size_of_subtree(sptree_##name *t, spnode_t node) { \ + if (node == SPNIL) \ + return 0; \ + return 1 + \ + sptree_##name##_size_of_subtree(t, _GET_SPNODE_LEFT(node)) + \ + sptree_##name##_size_of_subtree(t, _GET_SPNODE_RIGHT(node)); \ +} \ + \ +static inline spnode_t \ +sptree_##name##_get_place(sptree_##name *t) { \ + spnode_t node; \ + if (t->garbage_head != SPNIL) { \ + node = t->garbage_head; \ + t->garbage_head = _GET_SPNODE_LEFT(t->garbage_head); \ + } else { \ + if (t->nmember >= t->ntotal) { \ + t->ntotal *= 2; \ + t->members = realloc(t->members, t->ntotal * t->elemsize); \ + t->lrpointers = realloc(t->lrpointers, \ + t->ntotal * sizeof(sptree_node_pointers)); \ + } \ + \ + node = t->nmember; \ + t->nmember++; \ + } \ + _SET_SPNODE_LEFT(node, SPNIL); \ + _SET_SPNODE_RIGHT(node, SPNIL); \ + return node; \ +} \ + \ +static inline spnode_t \ +sptree_##name##_flatten_tree(sptree_##name *t, spnode_t root, spnode_t head){ \ + spnode_t node; \ + if (root == SPNIL) \ + return head; \ + node = sptree_##name##_flatten_tree(t, _GET_SPNODE_RIGHT(root), head); \ + _SET_SPNODE_RIGHT(root, node); \ + return sptree_##name##_flatten_tree(t, _GET_SPNODE_LEFT(root), root); \ +} \ + \ +static inline spnode_t \ +sptree_##name##_build_tree(sptree_##name *t, spnode_t node, spnode_t size) { \ + spnode_t tmp; \ + if (size == 0) { \ + _SET_SPNODE_LEFT(node, SPNIL); \ + return node; \ + } \ + spnode_t root = sptree_##name##_build_tree(t, \ + node, ceil(((double)size-1.0)/2.0)); \ + spnode_t list = sptree_##name##_build_tree(t, \ + _GET_SPNODE_RIGHT(root), floor(((double)size-1.0)/2.0)); \ + tmp = _GET_SPNODE_LEFT(list); \ + _SET_SPNODE_RIGHT(root, tmp); \ + _SET_SPNODE_LEFT(list, root); \ + \ + return list; \ +} \ + \ +static inline spnode_t \ +sptree_##name##_balance(sptree_##name *t, spnode_t node, spnode_t size) { \ + spnode_t fake = sptree_##name##_get_place(t); \ + spnode_t z; \ + \ + z = sptree_##name##_flatten_tree(t, node, fake); \ + sptree_##name##_build_tree(t, z, size); \ + \ + z = _GET_SPNODE_LEFT(fake); \ + _SET_SPNODE_LEFT(fake, t->garbage_head); \ + t->garbage_head = fake; \ + return z; \ +} \ + \ +static inline void \ +sptree_##name##_insert(sptree_##name *t, void *v) { \ + spnode_t node, depth = 0; \ + spnode_t path[ t->max_depth + 2]; \ + \ + if (t->root == SPNIL) { \ + _SET_SPNODE_LEFT(0, SPNIL); \ + _SET_SPNODE_RIGHT(0, SPNIL); \ + memcpy(t->members, v, t->elemsize); \ + t->root = 0; \ + t->garbage_head = SPNIL; \ + t->nmember = 1; \ + t->size=1; \ + return; \ + } else { \ + spnode_t parent = t->root; \ + \ + for(;;) { \ + int r = t->compare(v, ITHELEM(t, parent), t->arg); \ + if (r==0) { \ + memcpy(ITHELEM(t, parent), v, t->elemsize); \ + return; \ + } \ + path[depth] = parent; \ + depth++; \ + if (r>0) { \ + if (_GET_SPNODE_RIGHT(parent) == SPNIL) { \ + node = sptree_##name##_get_place(t); \ + memcpy(ITHELEM(t, node), v, t->elemsize); \ + _SET_SPNODE_RIGHT(parent, node); \ + break; \ + } else { \ + parent = _GET_SPNODE_RIGHT(parent); \ + } \ + } else { \ + if (_GET_SPNODE_LEFT(parent) == SPNIL) { \ + node = sptree_##name##_get_place(t); \ + memcpy(ITHELEM(t, node), v, t->elemsize); \ + _SET_SPNODE_LEFT(parent, node); \ + break; \ + } else { \ + parent = _GET_SPNODE_LEFT(parent); \ + } \ + } \ + } \ + } \ + \ + t->size++; \ + if ( t->size > t->max_size ) \ + t->max_size = t->size; \ + if ( depth > t->max_depth ) \ + t->max_depth = depth; \ + \ + if ( (double)depth > COUNTALPHA(t->size)) { \ + spnode_t parent; \ + spnode_t i, size = 1 ; \ + \ + path[depth] = node; \ + \ + for (i = 1; ; i++) { \ + if (i < depth) { \ + parent = path[ depth - i ]; \ + size += 1 + sptree_##name##_size_of_subtree( t, \ + _GET_SPNODE_RIGHT(parent) == path[depth - i + 1] ? \ + _GET_SPNODE_LEFT(parent) : _GET_SPNODE_RIGHT(parent)); \ + if ((double)i > COUNTALPHA(size)) { \ + spnode_t n = sptree_##name##_balance(t, parent, size); \ + spnode_t pp = path[ depth - i - 1 ]; \ + if (_GET_SPNODE_LEFT(pp) == parent) \ + _SET_SPNODE_LEFT(pp, n); \ + else \ + _SET_SPNODE_RIGHT(pp, n); \ + break; \ + } \ + } else { \ + t->root = sptree_##name##_balance(t, t->root, t->size); \ + t->max_size = t->size; \ + break; \ + } \ + } \ + } \ +} \ + \ +static inline void \ +sptree_##name##_delete(sptree_##name *t, void *k) { \ + spnode_t node = t->root; \ + spnode_t parent = SPNIL; \ + int lr = 0; \ + while(node != SPNIL) { \ + int r = t->compare(k, ITHELEM(t, node), t->arg); \ + if (r > 0) { \ + parent = node; \ + node = _GET_SPNODE_RIGHT(node); \ + lr = +1; \ + } else if (r < 0) { \ + parent = node; \ + node = _GET_SPNODE_LEFT(node); \ + lr = -1; \ + } else {/* found */ \ + if (_GET_SPNODE_LEFT(node) == SPNIL && _GET_SPNODE_RIGHT(node) == SPNIL) { \ + if ( parent == SPNIL ) \ + t->root = SPNIL; \ + else if (lr <0) \ + _SET_SPNODE_LEFT(parent, SPNIL); \ + else \ + _SET_SPNODE_RIGHT(parent, SPNIL); \ + } else if (_GET_SPNODE_LEFT(node) == SPNIL) { \ + spnode_t child = _GET_SPNODE_RIGHT(node); \ + if (parent == SPNIL) t->root = child; \ + else if (lr <0) _SET_SPNODE_LEFT(parent, child); \ + else _SET_SPNODE_RIGHT(parent, child); \ + } else if (_GET_SPNODE_RIGHT(node) == SPNIL) { \ + spnode_t child = _GET_SPNODE_LEFT(node); \ + if (parent == SPNIL) t->root = child; \ + else if (lr <0) _SET_SPNODE_LEFT(parent, child); \ + else _SET_SPNODE_RIGHT(parent, child); \ + } else { \ + spnode_t todel = _GET_SPNODE_LEFT(node); \ + \ + parent = SPNIL; \ + for(;;) { \ + if ( _GET_SPNODE_RIGHT(todel) != SPNIL ) { \ + parent = todel; \ + todel = _GET_SPNODE_RIGHT(todel); \ + } else \ + break; \ + } \ + memcpy(ITHELEM(t, node), ITHELEM(t, todel), t->elemsize); \ + if (parent != SPNIL) \ + _SET_SPNODE_RIGHT(parent, _GET_SPNODE_LEFT(todel)); \ + else \ + _SET_SPNODE_LEFT(node, _GET_SPNODE_LEFT(todel)); \ + node = todel; /* node to delete */ \ + } \ + \ + _SET_SPNODE_LEFT(node, t->garbage_head); \ + t->garbage_head = node; \ + \ + break; \ + } \ + } \ + \ + if (node == SPNIL) /* not found */ \ + return; \ + \ + t->size --; \ + if ( t->size > 0 && (double)t->size < alpha * t->max_size ) { \ + t->root = sptree_##name##_balance(t, t->root, t->size); \ + t->max_size = t->size; \ + } \ +} \ + \ +static inline spnode_t \ +sptree_##name##_walk(sptree_##name *t, void* array, spnode_t limit, spnode_t offset) { \ + int level = 0; \ + spnode_t count= 0, \ + node, \ + stack[ t->max_depth + 1 ]; \ + \ + if (t->root == SPNIL) return 0; \ + stack[0] = t->root; \ + \ + while( (node = _GET_SPNODE_LEFT( stack[level] )) != SPNIL ) { \ + level++; \ + stack[level] = node; \ + } \ + \ + while( count < offset + limit && level >= 0 ) { \ + \ + if (count >= offset) \ + memcpy(array + (count-offset) * t->elemsize, \ + ITHELEM(t, stack[level]), t->elemsize); \ + count++; \ + \ + node = _GET_SPNODE_RIGHT( stack[level] ); \ + level--; \ + while( node != SPNIL ) { \ + level++; \ + stack[level] = node; \ + node = _GET_SPNODE_LEFT( stack[level] ); \ + } \ + } \ + \ + return (count > offset) ? count - offset : 0; \ +} \ + \ +static inline void \ +sptree_##name##_walk_cb(sptree_##name *t, int (*cb)(void*, void*), void *cb_arg ) { \ + int level = 0; \ + spnode_t node, \ + stack[ t->max_depth + 1 ]; \ + \ + if (t->root == SPNIL) return; \ + stack[0] = t->root; \ + \ + while( (node = _GET_SPNODE_LEFT( stack[level] )) != SPNIL ) { \ + level++; \ + stack[level] = node; \ + } \ + \ + while( level >= 0 ) { \ + if ( cb(cb_arg, ITHELEM(t, stack[level])) == 0 ) \ + return; \ + \ + node = _GET_SPNODE_RIGHT( stack[level] ); \ + level--; \ + while( node != SPNIL ) { \ + level++; \ + stack[level] = node; \ + node = _GET_SPNODE_LEFT( stack[level] ); \ + } \ + } \ +} \ + \ +typedef struct sptree_##name##_iterator { \ + sptree_##name *t; \ + int level; \ + int max_depth; \ + spnode_t stack[0]; \ +} sptree_##name##_iterator; \ + \ +static inline sptree_##name##_iterator * \ +sptree_##name##_iterator_init(sptree_##name *t) { \ + sptree_##name##_iterator *i; \ + spnode_t node; \ + \ + if (t->root == SPNIL) return NULL; \ + \ + i = realloc(NULL, sizeof(*i) + sizeof(spnode_t) * (t->max_depth + 1)); \ + i->t = t; \ + i->level = 0; \ + i->stack[0] = t->root; \ + \ + while( (node = _GET_SPNODE_LEFT( i->stack[i->level] )) != SPNIL ) { \ + i->level++; \ + i->stack[i->level] = node; \ + } \ + \ + return i; \ +} \ + \ +static inline void \ +sptree_##name##_iterator_init_set(sptree_##name *t, sptree_##name##_iterator **i, void *k) { \ + spnode_t node; \ + int lastLevelEq = -1, cmp; \ + \ + if ((*i) == NULL || t->max_depth > (*i)->max_depth) \ + *i = realloc(*i, sizeof(**i) + sizeof(spnode_t) * (t->max_depth + 1)); \ + \ + (*i)->t = t; \ + if (t->root == SPNIL) return; \ + \ + (*i)->level = -1; \ + (*i)->max_depth = t->max_depth; \ + (*i)->stack[0] = t->root; \ + \ + node = t->root; \ + while(node != SPNIL) { \ + cmp = t->compare(k, ITHELEM(t, node), t->arg); \ + \ + (*i)->level++; \ + (*i)->stack[(*i)->level] = node; \ + \ + if (cmp > 0) { \ + (*i)->level--; /* exclude current node from path, ie "mark as visited" */ \ + node = _GET_SPNODE_RIGHT(node); \ + } else if (cmp < 0) { \ + node = _GET_SPNODE_LEFT(node); \ + } else { \ + lastLevelEq = (*i)->level; \ + node = _GET_SPNODE_LEFT(node); /* one way iterator: from left to right */ \ + } \ + } \ + \ + if (lastLevelEq >= 0) \ + (*i)->level = lastLevelEq; \ +} \ + \ +static inline void \ +sptree_##name##_iterator_free(sptree_##name##_iterator *i) { \ + if (i == NULL) return; \ + free(i); \ +} \ + \ +static inline void* \ +sptree_##name##_iterator_next(sptree_##name##_iterator *i) { \ + sptree_##name *t; \ + spnode_t node, returnNode = SPNIL; \ + \ + if (i == NULL) return NULL; \ + \ + t = i->t; \ + if ( i->level >= 0 ) { \ + returnNode = i->stack[i->level]; \ + \ + node = _GET_SPNODE_RIGHT( i->stack[i->level] ); \ + i->level--; \ + while( node != SPNIL ) { \ + i->level++; \ + i->stack[i->level] = node; \ + node = _GET_SPNODE_LEFT( i->stack[i->level] ); \ + } \ + } \ + \ + return (returnNode == SPNIL) ? NULL : ITHELEM(t, returnNode); \ +} + +#endif