/* Forward a simple one-liner command downstream. * For example, get, incr/decr, delete, etc. * The response, though, might be a simple line or * multiple VALUE+END lines. */ bool cproxy_forward_a2a_simple_downstream(downstream *d, char *command, conn *uc) { assert(d != NULL); assert(d->ptd != NULL); assert(d->ptd->proxy != NULL); assert(d->downstream_conns != NULL); assert(command != NULL); assert(uc != NULL); assert(uc->item == NULL); assert(uc->cmd_curr != -1); assert(d->multiget == NULL); assert(d->merger == NULL); // Handles get and gets. // if (uc->cmd_curr == PROTOCOL_BINARY_CMD_GET) { // Only use front_cache for 'get', not for 'gets'. // mcache *front_cache = (command[3] == ' ') ? &d->ptd->proxy->front_cache : NULL; return multiget_ascii_downstream(d, uc, a2a_multiget_start, a2a_multiget_skey, a2a_multiget_end, front_cache); } assert(uc->next == NULL); if (uc->cmd_curr == PROTOCOL_BINARY_CMD_FLUSH) return cproxy_broadcast_a2a_downstream(d, command, uc, "OK\r\n"); if (uc->cmd_curr == PROTOCOL_BINARY_CMD_STAT) { if (strncmp(command + 5, " reset", 6) == 0) return cproxy_broadcast_a2a_downstream(d, command, uc, "RESET\r\n"); if (cproxy_broadcast_a2a_downstream(d, command, uc, "END\r\n")) { d->merger = genhash_init(512, skeyhash_ops); return true; } else { return false; } } // TODO: Inefficient repeated scan_tokens. // int cmd_len = 0; token_t tokens[MAX_TOKENS]; size_t ntokens = scan_tokens(command, tokens, MAX_TOKENS, &cmd_len); char *key = tokens[KEY_TOKEN].value; int key_len = tokens[KEY_TOKEN].length; if (ntokens <= 1) { // This was checked long ago, while parsing assert(false); // the upstream conn. return false; } // Assuming we're already connected to downstream. // bool self = false; conn *c = cproxy_find_downstream_conn(d, key, key_len, &self); if (c != NULL) { if (self) { // TODO: This optimization could be done much earlier, // even before the upstream conn was assigned // to a downstream. // cproxy_optimize_to_self(d, uc, command); process_command(uc, command); return true; } if (cproxy_prep_conn_for_write(c)) { assert(c->state == conn_pause); out_string(c, command); if (settings.verbose > 1) fprintf(stderr, "forwarding to %d, noreply %d\n", c->sfd, uc->noreply); if (update_event(c, EV_WRITE | EV_PERSIST)) { d->downstream_used_start = 1; d->downstream_used = 1; if (cproxy_dettach_if_noreply(d, uc) == false) { cproxy_start_downstream_timeout(d, c); } else { c->write_and_go = conn_pause; // Do mcache_delete() here only during a noreply, // otherwise for with-reply requests, we could // be in a race with other clients repopulating // the front_cache. For with-reply requests, we // clear the front_cache when we get a success reply. // mcache_delete(&d->ptd->proxy->front_cache, key, key_len); } return true; } if (settings.verbose > 1) fprintf(stderr, "Couldn't update cproxy write event\n"); d->ptd->stats.stats.err_oom++; cproxy_close_conn(c); } else { d->ptd->stats.stats.err_downstream_write_prep++; cproxy_close_conn(c); } } return false; }
/* Forward a simple one-liner command downstream. * For example, get, incr/decr, delete, etc. * The response, though, might be a simple line or * multiple VALUE+END lines. */ bool cproxy_forward_a2a_simple_downstream(downstream *d, char *command, conn *uc) { cb_assert(d != NULL); cb_assert(d->ptd != NULL); cb_assert(d->ptd->proxy != NULL); cb_assert(d->downstream_conns != NULL); cb_assert(command != NULL); cb_assert(uc != NULL); cb_assert(uc->item == NULL); cb_assert(uc->cmd_curr != (protocol_binary_command) -1); cb_assert(d->multiget == NULL); cb_assert(d->merger == NULL); /* Handles get and gets. */ if (uc->cmd_curr == PROTOCOL_BINARY_CMD_GETK || uc->cmd_curr == PROTOCOL_BINARY_CMD_GETKQ || uc->cmd_curr == PROTOCOL_BINARY_CMD_GETL) { /* Only use front_cache for 'get', not for 'gets'. */ mcache *front_cache = (command[3] == ' ') ? &d->ptd->proxy->front_cache : NULL; return multiget_ascii_downstream(d, uc, a2a_multiget_start, a2a_multiget_skey, a2a_multiget_end, front_cache); } cb_assert(uc->next == NULL); if (uc->cmd_curr == PROTOCOL_BINARY_CMD_FLUSH) { return cproxy_broadcast_a2a_downstream(d, command, uc, "OK\r\n"); } if (uc->cmd_curr == PROTOCOL_BINARY_CMD_STAT) { if (strncmp(command + 5, " reset", 6) == 0) { return cproxy_broadcast_a2a_downstream(d, command, uc, "RESET\r\n"); } if (cproxy_broadcast_a2a_downstream(d, command, uc, "END\r\n")) { d->merger = genhash_init(512, skeyhash_ops); return true; } else { return false; } } /* TODO: Inefficient repeated scan_tokens. */ int cmd_len = 0; token_t tokens[MAX_TOKENS]; size_t ntokens = scan_tokens(command, tokens, MAX_TOKENS, &cmd_len); char *key = tokens[KEY_TOKEN].value; int key_len = tokens[KEY_TOKEN].length; if (ntokens <= 1) { /* This was checked long ago, while parsing */ cb_assert(false); /* the upstream conn. */ return false; } /* Assuming we're already connected to downstream. */ if (!strcmp(command, "version")) { /* fake key for version command handling */ key = "v"; key_len = 1; } conn *c = cproxy_find_downstream_conn(d, key, key_len, NULL); if (c != NULL) { if (cproxy_prep_conn_for_write(c)) { cb_assert(c->state == conn_pause); out_string(c, command); if (settings.verbose > 1) { moxi_log_write("forwarding to %d, noreply %d\n", c->sfd, uc->noreply); } if (update_event(c, EV_WRITE | EV_PERSIST)) { d->downstream_used_start = 1; d->downstream_used = 1; if (cproxy_dettach_if_noreply(d, uc) == false) { cproxy_start_downstream_timeout(d, c); } else { c->write_and_go = conn_pause; /* Do mcache_delete() here only during a noreply, */ /* otherwise for with-reply requests, we could */ /* be in a race with other clients repopulating */ /* the front_cache. For with-reply requests, we */ /* clear the front_cache when we get a success reply. */ cproxy_front_cache_delete(d->ptd, key, key_len); } return true; } if (settings.verbose > 1) { moxi_log_write("Couldn't update cproxy write event\n"); } d->ptd->stats.stats.err_oom++; cproxy_close_conn(c); } else { d->ptd->stats.stats.err_downstream_write_prep++; cproxy_close_conn(c); } } return false; }