-
Notifications
You must be signed in to change notification settings - Fork 161
[WIP] Add option to merge nats core and jetstream subscriptions #971
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
base: main
Are you sure you want to change the base?
Changes from 4 commits
8147ea1
cf3fbb7
e7958e5
30d0d82
7655acd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6351,6 +6351,9 @@ natsSubscription_SetOnCompleteCB(natsSubscription *sub, natsOnCompleteCB cb, voi | |
| NATS_EXTERN void | ||
| natsSubscription_Destroy(natsSubscription *sub); | ||
|
|
||
| NATS_EXTERN natsStatus | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would need documentation here... which would help me understand what this function is supposed to do ;-) |
||
| natsSubscription_CreateSharedSubscription(natsConnection *nc, jsCtx *js); | ||
|
|
||
| /** @} */ // end of subGroup | ||
|
|
||
| #if defined(NATS_HAS_STREAMING) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1370,10 +1370,158 @@ | |
| if (sub->jsi != NULL) | ||
| sub->jsi->dc = false; | ||
|
|
||
| if (sub->shareCount > 0) | ||
| sub->shareCount--; | ||
|
|
||
| natsSub_Unlock(sub); | ||
|
|
||
| // Don't close or free the subscription if it's still in use | ||
| if (sub->shareCount != 0) | ||
AdamPayzant marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return; | ||
|
|
||
| if (doUnsub) | ||
| (void)natsSubscription_Unsubscribe(sub); | ||
|
|
||
| natsSub_release(sub); | ||
| } | ||
|
|
||
| static void | ||
| _sharedRespHandler(natsConnection *nc, natsSubscription *sub, natsMsg *msg, void *closure) | ||
| { | ||
| char *rt = NULL; | ||
| const char *subj = NULL; | ||
| respInfo *resp = NULL; | ||
| bool dmsg = true; | ||
|
|
||
| js_handleSharedReply(nc, sub, msg, closure); | ||
|
||
|
|
||
| natsConn_Lock(nc); | ||
AdamPayzant marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if (natsConn_isClosed(nc)) | ||
| { | ||
| natsConn_Unlock(nc); | ||
| natsMsg_Destroy(msg); | ||
| return; | ||
| } | ||
| subj = natsMsg_GetSubject(msg); | ||
| // We look for the reply token by first checking that the message subject | ||
| // prefix matches the subscription's subject (without the last '*'). | ||
| // It is possible that it does not due to subject rewrite (JetStream). | ||
| if (((int) strlen(subj) > nc->reqIdOffset) | ||
| && (memcmp((const void*) sub->subject, (const void*) subj, strlen(sub->subject) - 1) == 0)) | ||
| { | ||
| rt = (char*) (natsMsg_GetSubject(msg) + nc->reqIdOffset); | ||
| resp = (respInfo*) natsStrHash_Remove(nc->respMap, rt); | ||
| } | ||
| else if (natsStrHash_Count(nc->respMap) == 1) | ||
| { | ||
| // Only if the subject is completely different, we assume that it | ||
| // could be the server that has rewritten the subject and so if there | ||
| // is a single entry, use that. | ||
| void *value = NULL; | ||
| natsStrHash_RemoveSingle(nc->respMap, NULL, &value); | ||
| resp = (respInfo*) value; | ||
| } | ||
| if (resp != NULL) | ||
| { | ||
| natsMutex_Lock(resp->mu); | ||
| // Check for the race where the requestor has already timed-out. | ||
| // If so, resp->removed will be true, in which case simply discard | ||
| // the message. | ||
| if (!resp->removed) | ||
| { | ||
|
|
||
| dmsg = false; | ||
| resp->msg = msg; | ||
| resp->removed = true; | ||
| natsCondition_Signal(resp->cond); | ||
| } | ||
| natsMutex_Unlock(resp->mu); | ||
| } | ||
| natsConn_Unlock(nc); | ||
|
|
||
| if (dmsg) | ||
| natsMsg_Destroy(msg); | ||
| } | ||
|
|
||
| natsStatus | ||
| natsSubscription_CreateSharedSubscription(natsConnection *nc, jsCtx *js) | ||
| { | ||
| natsSubscription *sub = NULL; | ||
| natsStatus s = NATS_OK; | ||
|
|
||
| // If either are already set, we shouldn't create a new one | ||
AdamPayzant marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if ((nc->respMux != NULL) || (js->rsub != NULL) || (js->nc != nc)) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reference to |
||
| return NATS_INVALID_ARG; | ||
| } | ||
|
|
||
| nc->respPool = NATS_CALLOC(RESP_INFO_POOL_MAX_SIZE, sizeof(respInfo*)); | ||
| if (nc->respPool == NULL) | ||
| s = nats_setDefaultError(NATS_NO_MEMORY); | ||
| if (s == NATS_OK) | ||
| s = natsStrHash_Create(&nc->respMap, 4); | ||
|
|
||
| s = natsCondition_Create(&(js->cond)); | ||
AdamPayzant marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
AdamPayzant marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| IFOK(s, natsStrHash_Create(&(js->pm), 64)); | ||
| if (s == NATS_OK) | ||
| { | ||
| js->rpre = NATS_MALLOC(js->rpreLen+1); | ||
| if (js->rpre == NULL) | ||
| s = nats_setDefaultError(NATS_NO_MEMORY); | ||
| else | ||
| { | ||
| char nuid[NUID_BUFFER_LEN+1]; | ||
|
|
||
| s = natsNUID_Next(nuid, sizeof(nuid)); | ||
| if (s == NATS_OK) | ||
| { | ||
| memcpy(js->rpre, js->nc->inboxPfx, js->nc->inboxPfxLen); | ||
| memcpy(js->rpre+js->nc->inboxPfxLen, nuid+((int)strlen(nuid)-8), 8); | ||
| js->rpre[js->rpreLen-1] = '.'; | ||
| js->rpre[js->rpreLen] = '\0'; | ||
| } | ||
| } | ||
| } | ||
| if (s == NATS_OK) | ||
| { | ||
| char *subj = NULL; | ||
|
|
||
| if (nats_asprintf(&subj, "%s*", js->rpre) < 0) | ||
| s = nats_setDefaultError(NATS_NO_MEMORY); | ||
| else | ||
| { | ||
| s = natsConn_subscribeNoPool(&sub, js->nc, subj, _sharedRespHandler, (void*) js); | ||
| if (s == NATS_OK) | ||
| { | ||
| nc->respSub = subj; | ||
| nc->reqIdOffset = js->rpreLen; | ||
|
|
||
| sub->shareCount = 2; | ||
| nc->respMux = sub; | ||
| js->rsub = sub; | ||
| } | ||
| else { | ||
| NATS_FREE(subj); | ||
| } | ||
| } | ||
|
|
||
| if (s == NATS_OK) | ||
| { | ||
| _retain(js); | ||
| natsSubscription_SetPendingLimits(js->rsub, -1, -1); | ||
| natsSubscription_SetOnCompleteCB(js->rsub, js_subComplete, (void*) js); | ||
| } | ||
| } | ||
| if (s != NATS_OK) | ||
| { | ||
| // Undo the things we created so we retry again next time. | ||
| // It is either that or we have to always check individual | ||
| // objects to know if we have to create them. | ||
| NATS_FREE(js->rpre); | ||
| js->rpre = NULL; | ||
| natsStrHash_Destroy(js->pm); | ||
| js->pm = NULL; | ||
| natsCondition_Destroy(js->cond); | ||
| js->cond = NULL; | ||
| } | ||
| return NATS_UPDATE_ERR_STACK(s); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42397,3 +42397,57 @@ int main(int argc, char **argv) | |
| printf("ALL PASSED\n"); | ||
| return 0; | ||
| } | ||
|
|
||
| void test_JetStreamSharedSub(void) | ||
| { | ||
| natsStatus s; | ||
| natsMsg *msg = NULL; | ||
| natsMsg *req = NULL; | ||
| struct threadArg arg; | ||
| natsSubscription *sub = NULL; | ||
|
|
||
| JS_SETUP(2, 12, 0); | ||
|
|
||
| s = _createDefaultThreadArgsForCbTests(&arg); | ||
| if ( s != NATS_OK) | ||
| FAIL("Unable to setup test!"); | ||
|
|
||
| arg.string = "I will help you"; | ||
| arg.status = NATS_OK; | ||
| arg.control= 4; | ||
|
|
||
| test("Creating shared subscription: "); | ||
| s = natsSubscription_CreateSharedSubscription(nc, js); | ||
| testCond(s == NATS_OK); | ||
|
|
||
| test("Subscribe: "); | ||
| IFOK(s, natsConnection_Subscribe(&sub, nc, "foo", _recvTestString, (void*) &arg)); | ||
| testCond(s == NATS_OK); | ||
|
|
||
| test("Validate Jetstream Publish: "); | ||
| s = natsMsg_Create(&req, "foo", NULL, "help", 4); | ||
| IFOK(s, js_PublishMsgAsync(js, &req, NULL)); | ||
| testCond(s == NATS_OK); | ||
|
|
||
| test("Wait for publish ack: "); | ||
| s = js_PublishAsyncComplete(js, NULL); | ||
| testCond(s == NATS_OK); | ||
|
|
||
| test("Create req message: "); | ||
AdamPayzant marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| s = natsMsg_Create(&req, "foo", NULL, "help", 4); | ||
| testCond(s == NATS_OK); | ||
|
|
||
| test("Validate Core RequestMsg: "); | ||
| s = natsConnection_RequestMsg(&msg, nc, req, 100); | ||
| natsMutex_Lock(arg.m); | ||
| while ((s != NATS_TIMEOUT) && !arg.msgReceived) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would not the I am not entirely sure what you are trying to test here. |
||
| s = natsCondition_TimedWait(arg.c, arg.m, 2000); | ||
| testCond(s == NATS_OK); | ||
AdamPayzant marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| natsMsg_Destroy(req); | ||
| natsMsg_Destroy(msg); | ||
|
|
||
| natsSubscription_Destroy(sub); | ||
| _destroyDefaultThreadArgs(&arg); | ||
|
|
||
| JS_TEARDOWN; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.