Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make twemcache deal with partial request value #124

Merged
merged 13 commits into from
Feb 25, 2017

Conversation

thinkingfish
Copy link
Contributor

This is the first diff to address issue #122

Subsequent diff will add more unit tests, as well as handle partial value when writing responses (when wbuf is full)

Copy link
Contributor

@seppo0010 seppo0010 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel as this change could use a different type of test, with a client sending messages instead of the unit tests we've been writing.

@@ -23,6 +23,7 @@ test_setup(void)
{
option_load_default((struct option *)&options, OPTION_CARDINALITY(options));
slab_setup(&options, &metrics);
printf("foo\n");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uhm, probably not meant to be commited

ck_assert_int_eq(it->klen, sizeof(KEY) - 1);
ck_assert_int_eq(it->dataflag, dataflag);
ck_assert_int_eq(it->vlen, val.len);
ck_assert_msg(strspn(item_data(it), "A") == val.len, "item_data contains wrong value %.*s", it->vlen, item_data(it));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think item_data(it) is null terminated

key = str2bstr(KEY);

vlen = 1000 * KiB;
val.len = vlen / 2;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel the test would be better if val.len was not exactly the half of vlen as it will make both sides to be the same length coincidentally, and an error may be missed because of it.

This comment was marked as spam.

free(val.data);
ck_assert_msg(!it->is_linked, "item linked by mistake");
ck_assert_int_eq(it->vlen, vlen);
ck_assert_msg(strspn(item_data(it) + vlen - val.len, "B") == val.len,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add an assertion on the first part of the item_data to make sure it was not modified?

}
END_TEST

START_TEST(test_evict_refcount)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this test exactly the same as the one above?

This comment was marked as spam.

item_delete(key);
status = item_insert(key, &(req->vstr), req->flag, time_reltime(req->expiry));
if ((it = item_get(key)) != NULL) {
status =_generic_set(req, true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing space after =

@@ -262,8 +288,7 @@ _process_cas(struct response *rsp, struct request *req)
rsp->type = RSP_EXISTS;
INCR(process_metrics, cas_exists);
} else {
item_delete(key);
status = item_insert(key, &(req->vstr), req->flag, time_reltime(req->expiry));
status =_generic_set(req, true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing space after =

@@ -207,7 +233,7 @@ _process_add(struct response *rsp, struct request *req)
rsp->type = RSP_NOT_STORED;
INCR(process_metrics, add_notstored);
} else {
status = item_insert(key, &(req->vstr), req->flag, time_reltime(req->expiry));
status =_generic_set(req, false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing space after =

item_delete(key);
status = item_insert(key, &(req->vstr), req->flag, time_reltime(req->expiry));

status =_generic_set(req, true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing space after =

@@ -39,6 +39,10 @@ request_reset(struct request *req)
req->delta = 0;
req->vcas = 0;

req->partial = false; /* only set to true when partial value received */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

having a rstate that may be REQ_PARTIAL this sees either redundant, or at least confusing... looking at the change I can see partial being set to false only when rstate going from REQ_PARTIAL to REQ_PARSED and set to true only when rstate is set to REQ_PARTIAL... isn't then this boolean redundant?

@thinkingfish

This comment was marked as spam.

@seppo0010
Copy link
Contributor

We can have test dependencies that are not used for the binaries, in fact we should already have that for libcheck, and not require them for building the binaries. I believe it should be on the same repo, as they may need to get updated with code changes and that seems to be the common practice in other projects (e.g.: node projects have a devDependencies in package.json, rust projects have dev-dependencies in Cargo.toml and so on; also for example Redis repo has its tcl integration tests included).

Given this specific use case for partial requests, I don't believe any memcached client will expose an appropriated interface for the test we need, which is unfortunate, so I believe any scripting language will be good enough.

@thinkingfish thinkingfish self-assigned this Feb 8, 2017
buf, buf_rsize(buf), vlen + CRLF_LEN);

return PARSE_EUNFIN;
if (rsize < nbyte + CRLF_LEN) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since rsize and nbyte do not change before checking the value of complete, can you move this branch below, get rid of complete and use an else instead of if (complete) ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if I made myself clear, something like this:

diff --git a/src/protocol/data/memcache/parse.c b/src/protocol/data/memcache/parse.c
index cf3c06b..66cff18 100644
--- a/src/protocol/data/memcache/parse.c
+++ b/src/protocol/data/memcache/parse.c
@@ -277,21 +277,18 @@ _parse_val(struct bstring *val, struct buf *buf, uint32_t nbyte)
 {
     parse_rstatus_t status;
     uint32_t rsize = buf_rsize(buf);
-    bool complete = true;
 
     log_verb("parsing val (string) at %p", buf->rpos);
 
-    if (rsize < nbyte + CRLF_LEN) {
-        complete = false;
-        status = PARSE_EUNFIN;
-    }
-
     val->len = MIN(nbyte, rsize);
     val->data = (val->len > 0) ? buf->rpos : NULL;
     buf->rpos += val->len;
 
-    /* verify CRLF */
-    if (complete) { /* _try_crlf cannot return EUNFIN in this case */
+
+    if (rsize < nbyte + CRLF_LEN) {
+        status = PARSE_EUNFIN;
+    } else {
+        /* _try_crlf cannot return EUNFIN in this case */
         status = _try_crlf(buf, buf->rpos);
         if (status == PARSE_OK) {
             buf->rpos += CRLF_LEN;

This comment was marked as spam.

buf->rpos - old_rpos);
/* return and start from beginning next time */
request_reset(req);
buf->rpos = old_rpos;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, but I think we're missing a break; here? otherwise it will enter the next if (as status is not PARSE_OK) and it will set the rstate to REQ_PARSED.

This comment was marked as spam.

key = array_first(req->keys);
if (item_get(key) != NULL) {
it = (struct item *)req->reserved;
key = (struct bstring){it->klen, item_key(it)};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if item_reserve failed in _put, req->reserved will be null, and all this code will fail; check for status != PUT_ERROR earlier

This comment was marked as spam.

item_delete(key);
status = item_insert(key, &(req->vstr), req->flag, time_reltime(req->expiry));
if (status == ITEM_OK) {
it = (struct item *)req->reserved;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here ^^

This comment was marked as spam.

key = array_first(req->keys);
it = item_get(key);
if (it == NULL) {
it = (struct item *)req->reserved;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^^

This comment was marked as spam.

item_delete(key);
status = item_insert(key, &nval, dataflag, it->expire_at);
status = item_reserve(&it, key, &nval, nval.len, dataflag,
it->expire_at);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check for status before inserting?

This comment was marked as spam.

_error_rsp(rsp, istatus);
INCR(process_metrics, set_ex);
INCR(process_metrics, cas_ex);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cas_ex?

This comment was marked as spam.

@@ -242,23 +245,25 @@ _process_set(struct response *rsp, struct request *req)
struct item *it;
struct bstring key;

status = _put(&istatus, req);
status = _put(&istatus, req, true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are you deleting the existing item if set fails? If I understand this correctly, failing to set key to value2 will delete existing value1, is that so?

This comment was marked as spam.

This comment was marked as spam.

Copy link
Contributor

@seppo0010 seppo0010 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, a couple of minor comments

I'd love an integration test 😄 and it'd be nice if someone else can review it, as this change is quite large

int ret;
struct bstring key = str2bstr(KEY);
struct bstring val1 = str2bstr(VAL1);
struct bstring val2 = str2bstr(VAL2);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer if VAL1 and VAL2 were not the same length as it can hide some arithmetic bug like subtracting remaining number of bytes instead of received bytes

This comment was marked as spam.

#undef EXPIRY
#undef FLAG
#undef VAL
#undef KEY
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EXPIRY, FLAG, VAL and KEY were not defined, remove undef

This comment was marked as spam.

@thinkingfish

This comment was marked as spam.

@thinkingfish

This comment was marked as spam.

@thinkingfish thinkingfish merged commit a06894f into twitter:master Feb 25, 2017
@thinkingfish thinkingfish deleted the partial_value branch February 25, 2017 04:48
swlynch99 pushed a commit to swlynch99/pelikan-twitter that referenced this pull request Sep 30, 2019
remove install instructions; get rid of cmake warning
brayniac pushed a commit to brayniac/pelikan-twitter that referenced this pull request Nov 5, 2019
4a3a0fd Merge pull request twitter#124 from thinkingfish/remove_install
d2964bd Merge pull request twitter#122 from paegun/fix-ubuntu-test-linkage
481597c remove install instructions (will re-add later); get rid of cmake warning
74d9db8 reordered pthread dependency to follow check, the earliest dependent lib.
d5c2fec fixed test linkage for ubuntu, tested on 14.04. added support for test-coverage.sh to use xdg-open rather than open when present, ie on ubuntu.

git-subtree-dir: deps/ccommon
git-subtree-split: 4a3a0fd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants