/* Responding to a message with a sender id different to the previous one received is now straightforward. We simply accept that this is the next expected message and reset the context state - all is OK */ static mama_status handleFTTakeover (dqStrategy strategy, mamaMsg msg, int msgType, mamaDqContext* ctx, mama_seqnum_t seqNum, mama_u64_t senderId, int recoverOnRecap) { const char* symbol = NULL; mamaSubscription_getSymbol (self->mSubscription, &symbol); mama_log (MAMA_LOG_LEVEL_NORMAL, "Detected FT takeover. " "Original SenderId: %llu. New SenderId: %llu. " "Previous SeqNum: %u. New SeqNum: %u. [%s]", ctx->mSenderId, senderId, ctx->mSeqNum, seqNum, symbol); if (recoverOnRecap) { ctx->mSeqNum = seqNum; ctx->mDQState = DQ_STATE_WAITING_FOR_RECAP_AFTER_FT; } else { resetDqState (strategy, ctx); /*In all cases we reset the data quality context*/ dqStrategyImpl_resetDqContext (ctx, seqNum, senderId); } return MAMA_STATUS_OK; }
static void resetDqState (dqStrategy strategy, mamaDqContext* ctx) { if (ctx->mDQState != DQ_STATE_OK && ctx->mDQState != DQ_STATE_NOT_ESTABLISHED) { void* closure = NULL; mamaMsgCallbacks* cb = NULL; mamaSubscription_getClosure (self->mSubscription, &closure); /* Callback last in the event that client destroys */ cb = mamaSubscription_getUserCallbacks (self->mSubscription); if (cb != NULL && cb->onQuality != NULL) { const char* symbol = NULL; short cause; const void* platformInfo = NULL; mamaSubscription_getSymbol (self->mSubscription, &symbol); mamaSubscription_getAdvisoryCauseAndPlatformInfo ( self->mSubscription, &cause, &platformInfo); cb->onQuality (self->mSubscription, MAMA_QUALITY_OK, symbol, cause, platformInfo, closure); } } ctx->mDQState = DQ_STATE_OK; }
/* * Call this routine when subscription error occurs */ void onError(mamaSubscription subscription, mama_status status, void * platformError, const char * subject, void * closure) { const char * topicName = NULL; if (mamaSubscription_getSymbol(subscription, &topicName) == MAMA_STATUS_OK) printf("Error occured with subscription to topic \"%s\", error code: %s\n", topicName, mamaStatus_stringForStatus(status)); }
/* * Call this routine when subscription is created */ void onCreate(mamaSubscription subscription, void * closure) { const char * topicName = NULL; if (mamaSubscription_getSymbol(subscription, &topicName) == MAMA_STATUS_OK) { printf("Created subscription to topic \"%s\"\n", topicName); } }
static void MAMACALLTYPE mamaDictionaryDefaultCallback_onError ( mamaDictionary dict, const char* errorStr, void *closure ) { mamaDictionaryImpl* impl = (mamaDictionaryImpl*)dict; const char* userSymbol = NULL; mamaSubscription_getSymbol (impl->mSubscription, &userSymbol); mama_log (MAMA_LOG_LEVEL_NORMAL, "%s%s %s", userSymbolFormatted, errorStr != NULL ? errorStr : "" ); }
/* These are the default callback methods. Users may specify an alternative * set of callbacks by calling the mamaDictionary_setXXXCallback() * methods. */ static void MAMACALLTYPE mamaDictionaryDefaultCallback_onTimeout( mamaDictionary dict, void *closure ) { mamaDictionaryImpl* impl = (mamaDictionaryImpl*)dict; const char* userSymbol = NULL; mamaSubscription_getSymbol (impl->mSubscription, &userSymbol); mama_log (MAMA_LOG_LEVEL_NORMAL, "%s%s Error: mamaDictionary: timed out waiting for data dictionary.", userSymbolFormatted); }
static mama_status handleStaleData (dqStrategy strategy, mamaMsg msg, mamaDqContext* ctx) { const char* symbol = NULL; mamaSubscription_getSymbol (self->mSubscription, &symbol); if (gMamaLogLevel >= MAMA_LOG_LEVEL_FINER) { symbol = symbol == NULL ? "" : symbol; mama_log (MAMA_LOG_LEVEL_FINER, "%s : stale data", symbol); } msgUtils_setStatus (msg, MAMA_MSG_STATUS_STALE); if (ctx->mDQState != DQ_STATE_WAITING_FOR_RECAP && ctx->mDQState != DQ_STATE_POSSIBLY_STALE) { void* closure = NULL; mamaMsgCallbacks* cb = NULL; dqStrategy_sendRecapRequest (self, msg, ctx); /* Callback last in the event that client destroys */ mamaSubscription_getClosure (self->mSubscription, &closure); cb = mamaSubscription_getUserCallbacks (self->mSubscription); if (cb != NULL && cb->onQuality != NULL) { short cause; const void* platformInfo = NULL; mamaSubscription_getAdvisoryCauseAndPlatformInfo ( self->mSubscription, &cause, &platformInfo); cb->onQuality (self->mSubscription, MAMA_QUALITY_STALE, symbol, cause, platformInfo, closure); } } else if (ctx->mDQState == DQ_STATE_POSSIBLY_STALE && ctx->mDQState != DQ_STATE_WAITING_FOR_RECAP) { dqStrategy_sendRecapRequest (self, msg, ctx); } return MAMA_STATUS_OK; }
/* * Call this routine when a message is received */ void onMessage(mamaSubscription subscription, mamaMsg message, void * closure, void * itemClosure) { const char * topicName = NULL; if (mamaSubscription_getSymbol(subscription, &topicName) == MAMA_STATUS_OK) { printf("Message of type %s received on topic \"%s\"\n", mamaMsgType_stringForMsg(message), topicName); } // extract from the message our own custom field (see topicPublisherMultiple.c) const char * const MY_FIELD_NAME = "MdMyTimestamp"; const int MY_FIELD_ID = 99, BUFFER_SIZE = 32; char buffer[BUFFER_SIZE]; if (mamaMsg_getFieldAsString(message, MY_FIELD_NAME, MY_FIELD_ID, buffer, BUFFER_SIZE) == MAMA_STATUS_OK) { printf("This message has a field \"%s\" with value: %s", MY_FIELD_NAME, buffer); } }
static void determineMaxFid (mamaDictionary dictionary, mamaMsg msg) { const char* userSymbol; /* Determine the maximum field id */ mamaMsg_iterateFields (msg, maxIterator, NULL, self); if (dictionary->mSubscription) { mamaSubscription_getSymbol (dictionary->mSubscription, &userSymbol); mama_log (MAMA_LOG_LEVEL_FINE, "%s%s DICT maxFid: %d", userSymbolFormatted, self->mMaxFid); } else { mama_log (MAMA_LOG_LEVEL_FINE, "DICT maxFid: %d", self->mMaxFid); } }
mama_status dqContext_applyPreInitialCache (mamaDqContext* ctx, mamaSubscription subscription) { /*We looking for the next sequence number in the cache*/ mama_seqnum_t expectedSeqNum = ctx->mSeqNum + 1; int currentMessageindex = 0; const char* symbol = NULL; /*Ensure we have a cahce to iterate*/ if (ctx->mCache == NULL) return MAMA_STATUS_OK; mamaSubscription_getSymbol (subscription, &symbol); mama_log (MAMA_LOG_LEVEL_NORMAL, "%s: Attempting to apply pre initial cache after initial.", symbol); /*Loop from start to last added message - may have looped over*/ while (currentMessageindex<ctx->mCurCacheIdx) { mama_seqnum_t cachedMessageSeqNum = 0; /*just in case*/ if (!ctx->mCache[currentMessageindex]) return MAMA_STATUS_OK; mamaMsg_getSeqNum (ctx->mCache[currentMessageindex], &cachedMessageSeqNum); mama_log (MAMA_LOG_LEVEL_NORMAL, "%s: Found cached msg withseqNum: %d Current [%d]", symbol, cachedMessageSeqNum, ctx->mSeqNum); if (expectedSeqNum==cachedMessageSeqNum) { mama_log (MAMA_LOG_LEVEL_NORMAL, "%s: Applying cached message after initial", symbol); /*Next expected - pass it on!*/ mamaSubscription_forwardMsg(subscription, ctx->mCache[currentMessageindex]); /*this is now the latest sequence number for the context*/ ctx->mSeqNum = cachedMessageSeqNum; expectedSeqNum = cachedMessageSeqNum+1; /*Lets see if there are any more in the cache*/ } else /*Not the expected message - have a couple of options*/ { /*Bomb out - we can't apply this or any subsequent messages as they are stored in arrival order*/ if (cachedMessageSeqNum>expectedSeqNum) { mama_log (MAMA_LOG_LEVEL_NORMAL, "%s: Can't apply cached message", symbol); break; } } currentMessageindex++; } return MAMA_STATUS_OK; }
mama_status dqStrategy_checkSeqNum (dqStrategy strategy, mamaMsg msg, int msgType, mamaDqContext* ctx) { mama_seqnum_t seqNum = 0; mama_seqnum_t ctxSeqNum = ctx->mSeqNum; mama_u64_t senderId = 0; mama_u64_t ctxSenderId = ctx->mSenderId; dqState ctxDqState = ctx->mDQState; mamaSubscription subscription = self->mSubscription; mama_u16_t conflateCnt = 1; wombat_subscriptionGapCB onGap = NULL; mamaTransport transport; mamaStatsCollector transportStatsCollector = NULL; wombat_subscriptionQualityCB onQuality = NULL; mamaMsgStatus msgStatus = MAMA_MSG_STATUS_UNKNOWN; mamaTransport tport = NULL; mamaSubscription_getTransport (self->mSubscription, &tport); mamaMsg_getSeqNum (msg, &seqNum); ctx->mDoNotForward = 0; if (mamaMsg_getU64 (msg, MamaFieldSenderId.mName, MamaFieldSenderId.mFid, &senderId) != MAMA_STATUS_OK) { /* We just ignore it as we might be running against an older FH */ senderId = 0; } /*Special case for dealing with a fault tolerant takeover*/ if (ctxSenderId != 0 && senderId != 0 && ctxSenderId != senderId) { /* Only record FT Takeovers per transport and globally */ if (gGenerateTransportStats) { mamaSubscription_getTransport (subscription, &transport); transportStatsCollector = mamaTransport_getStatsCollector (transport); mamaStatsCollector_incrementStat (transportStatsCollector, MamaStatFtTakeovers.mFid); } if (mamaInternal_getGlobalStatsCollector() != NULL) { mamaStatsCollector_incrementStat (mamaInternal_getGlobalStatsCollector(), MamaStatFtTakeovers.mFid); } if (DQ_FT_WAIT_FOR_RECAP==mamaTransportImpl_getFtStrategyScheme(tport)) { ctx->mDoNotForward = 1; handleFTTakeover (strategy, msg, msgType, ctx, seqNum, senderId, 1); } else { return handleFTTakeover (strategy, msg, msgType, ctx, seqNum, senderId, 0); } } if (gMamaLogLevel >= MAMA_LOG_LEVEL_FINEST) { const char* symbol = NULL; mamaSubscription_getSymbol (subscription, &symbol); symbol = symbol == NULL ? "" : symbol; mama_log (MAMA_LOG_LEVEL_FINEST, "dqStrategy_checkSeqNum(): %s : seq# %ld", symbol, seqNum); } switch (msgType) { case MAMA_MSG_TYPE_QUOTE: case MAMA_MSG_TYPE_TRADE: case MAMA_MSG_TYPE_UPDATE: default: if (MAMA_STATUS_OK != mamaMsg_getU16 (msg, MamaFieldConflateCount.mName, MamaFieldConflateCount.mFid, &conflateCnt)) { /*Need to set conflateCnt=1 as mamaMsg_getU16 sets conflateCnt=0 if not found */ conflateCnt = 1; } /* Deliberate fallthrough. Only the types above can be conflated, so only try to extract the conflate count field for those types. */ case MAMA_MSG_TYPE_SEC_STATUS: case MAMA_MSG_TYPE_BOOK_UPDATE: if (((ctxDqState == DQ_STATE_NOT_ESTABLISHED) || (seqNum == 0) || (seqNum == (ctxSeqNum + conflateCnt))) && ((ctxDqState != DQ_STATE_WAITING_FOR_RECAP) || (ctxDqState != DQ_STATE_WAITING_FOR_RECAP_AFTER_FT))) { /* No gap */ if (self->mTryToFillGap) { self->mTryToFillGap = 0; dqContext_clearCache (ctx, 0); } /* It is no longer the case that all subscriptions are possibly stale. */ mamaSubscription_unsetAllPossiblyStale (subscription); /* If the sequence numbers for a message are correct then the subscription is OK. */ msgStatus = mamaMsgStatus_statusForMsg (msg); /* Check the status of the message. If it is stale, do not request a recap and do not set status OK. */ if (msgStatus == MAMA_MSG_STATUS_OK) { resetDqState (strategy, ctx); ctx->mSeqNum = seqNum; return MAMA_STATUS_OK; } } /* For late joins or middlewares that support a publish cache, it is possible that you will get old updates in this case take no action */ if (DQ_SCHEME_INGORE_DUPS == mamaTransportImpl_getDqStrategyScheme(tport)) { if ((seqNum <= ctxSeqNum) && ((ctx->mDQState != DQ_STATE_WAITING_FOR_RECAP) && (ctx->mDQState != DQ_STATE_WAITING_FOR_RECAP_AFTER_FT))) { ctx->mDoNotForward = 1; return MAMA_STATUS_OK; } } if ((seqNum == ctxSeqNum) && (ctxDqState != DQ_STATE_WAITING_FOR_RECAP_AFTER_FT)) { /* Duplicate data - set DQQuality to DUPLICATE, invoke quality callback */ ctx->mDQState = DQ_STATE_DUPLICATE; if ((onQuality = (mamaSubscription_getUserCallbacks (subscription))->onQuality)) { void* closure = NULL; const char* symbol = NULL; short cause; const void* platformInfo = NULL; mamaSubscription_getClosure (subscription, &closure); mamaSubscription_getSymbol (subscription, &symbol); mamaSubscription_getAdvisoryCauseAndPlatformInfo ( subscription, &cause, &platformInfo); onQuality (subscription, MAMA_QUALITY_DUPLICATE, symbol, cause, platformInfo, closure); } msgUtils_setStatus (msg, MAMA_MSG_STATUS_DUPLICATE); return MAMA_STATUS_OK; } if (ctxDqState == DQ_STATE_WAITING_FOR_RECAP_AFTER_FT) { ctx->mDoNotForward = 1; return MAMA_STATUS_OK; } else { /* If we get here, we missed a sequence number. */ if ((PRE_INITIAL_SCHEME_ON_GAP==mamaTransportImpl_getPreInitialScheme(tport)) &&(self->mTryToFillGap)) { self->mTryToFillGap = 0; if (dqContext_fillGap (ctx, seqNum, subscription)) { /* we filled it */ dqContext_clearCache (ctx, 0); ctx->mSeqNum = seqNum; return MAMA_STATUS_OK; } dqContext_clearCache (ctx, 0); } if (gMamaLogLevel >= MAMA_LOG_LEVEL_FINE) { const char* symbol = NULL; mamaSubscription_getSymbol (subscription, &symbol); symbol = symbol == NULL ? "" : symbol; mama_log (MAMA_LOG_LEVEL_FINE, "%s : SeqNum gap (%ld-%ld)", symbol, ctxSeqNum+1, seqNum-1); } if ((onGap = (mamaSubscription_getUserCallbacks (subscription)->onGap))) { void* closure = NULL; mamaSubscription_getClosure (subscription, &closure); onGap (subscription, closure); } handleStaleData (self, msg, ctx); } break; case MAMA_MSG_TYPE_INITIAL : case MAMA_MSG_TYPE_BOOK_INITIAL : msgStatus = MAMA_MSG_STATUS_UNKNOWN; self->mTryToFillGap = 1; msgStatus = mamaMsgStatus_statusForMsg (msg); /* Check the status of the message. If it is stale, do not request a recap and do not set status OK. */ if ((msgStatus == MAMA_MSG_STATUS_POSSIBLY_STALE) || (msgStatus == MAMA_MSG_STATUS_STALE)) { ctx->mDQState = DQ_STATE_STALE_NO_RECAP; dqStrategyImpl_resetDqContext (ctx, seqNum, senderId); return MAMA_STATUS_OK; } case MAMA_MSG_TYPE_RECAP : case MAMA_MSG_TYPE_BOOK_RECAP : /* For late joins or middlewares that support a publish cache, it is possible that you will get old updates in this case take no action */ if (DQ_SCHEME_INGORE_DUPS == mamaTransportImpl_getDqStrategyScheme(tport)) { if (MAMA_MSG_TYPE_RECAP == msgType) { /* Feed-handlers maintain sequence number for a Record FT Recap. */ if ((seqNum <= ctxSeqNum) && ((ctx->mDQState != DQ_STATE_WAITING_FOR_RECAP) && (ctx->mDQState != DQ_STATE_WAITING_FOR_RECAP_AFTER_FT))) { ctx->mDoNotForward = 1; return MAMA_STATUS_OK; } } else if (MAMA_MSG_TYPE_BOOK_RECAP == msgType) { if (0 == seqNum && ctxSeqNum > 0) { /* Special case of an FT Order Book Recap where a SeqNum of 0 is used. */ if (ctx->mDQState != DQ_STATE_WAITING_FOR_RECAP_AFTER_FT) { ctx->mDoNotForward = 1; return MAMA_STATUS_OK; } } /* Solicited Recap from Feed-Handler or * solicited / unsolicited Recap from mid-tier. */ else if ((seqNum <= ctxSeqNum) && ((ctx->mDQState != DQ_STATE_WAITING_FOR_RECAP) && (ctx->mDQState != DQ_STATE_WAITING_FOR_RECAP_AFTER_FT))) { ctx->mDoNotForward = 1; return MAMA_STATUS_OK; } } } if (mamaTransportImpl_preRecapCacheEnabled (tport)) { self->mTryToFillGap = 1; } mamaSubscription_unsetAllPossiblyStale (subscription); resetDqState (strategy, ctx); dqStrategyImpl_resetDqContext (ctx, seqNum, senderId); ctx->mDoNotForward = 0; return MAMA_STATUS_OK; case MAMA_MSG_TYPE_DDICT_SNAPSHOT : /*No DQ checking for Datadictionary*/ return MAMA_STATUS_OK; } return MAMA_STATUS_OK; }