=== modified file 'storage/ndb/src/kernel/blocks/ERROR_codes.txt' --- storage/ndb/src/kernel/blocks/ERROR_codes.txt 2008-12-08 13:58:15 +0000 +++ storage/ndb/src/kernel/blocks/ERROR_codes.txt 2008-12-08 22:17:08 +0000 @@ -3,10 +3,10 @@ Next NDBCNTR 1002 Next NDBFS 2000 Next DBACC 3002 Next DBTUP 4029 -Next DBLQH 5051 +Next DBLQH 5053 Next DBDICT 6013 Next DBDIH 7216 -Next DBTC 8074 +Next DBTC 8078 Next CMVMI 9000 Next BACKUP 10041 Next DBUTIL 11002 @@ -354,6 +354,25 @@ ABORT OF TCKEYREQ 8051 : Simulate failure of allocation for saveINDXKEYINFO +DBTC LONG SIGNAL TESTING +------------------------ +8065: Consume all but 10 long section segments on next TCKEYREQ +8066: Consume all but 1 long section segments on next TCKEYREQ +8067: Consume all long section segments on next TCKEYREQ +8068: Free all segments hoarded by 8065, 8066, 8067 + +8069: Always send 'short' LQHKEYREQ to LQH +8070: Always send 'short' SCANFRAGREQ to LQH + +8074: Drop first fragment of fragmented SCANTABREQ +8075: Drop middle fragments of fragmented SCANTABREQ +8076: Drop last fragment of fragmented SCANTABREQ +8077: Drop all fragments of fragmented SCANTABREQ + +DBLQH LONG SIGNAL TESTING +------------------------- +5051: Send short LQHKEYREQ to next replica +5052: Fail to store KeyInfo from TUP in long section CMVMI ----- === modified file 'storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp' --- storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp 2008-12-08 13:58:15 +0000 +++ storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp 2008-12-08 15:17:42 +0000 @@ -3661,6 +3661,12 @@ void Dblqh::execSIGNAL_DROPPED_REP(Signa * long signal buffering to store its sections */ jamEntry(); + + if (!assembleDroppedFragments(signal)) + { + jam(); + return; + } const SignalDroppedRep* rep = (SignalDroppedRep*) &signal->theData[0]; Uint32 originalGSN= rep->originalGsn; === modified file 'storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp' --- storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp 2008-12-03 19:51:33 +0000 +++ storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp 2008-12-08 17:36:07 +0000 @@ -1870,6 +1870,8 @@ private: bool validate_filter(Signal*); bool match_and_print(Signal*, ApiConnectRecordPtr); + bool testFragmentDrop(Signal* signal); + // For Error inserts Uint32 errorInsertHoardedSegments; === modified file 'storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp' --- storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp 2008-12-08 13:58:15 +0000 +++ storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp 2008-12-08 22:30:21 +0000 @@ -2606,7 +2606,7 @@ void Dbtc::execTCKEYREQ(Signal* signal) ERROR_INSERTED(8066) || ERROR_INSERTED(8067)) { - /* Consume all but 10(8065) or all but 1 (8066) or all (8077) of + /* Consume all but 10(8065) or all but 1 (8066) or all (8067) of * the SegmentedSection buffers to allow testing of what happens * when they're exhausted, either in this signal or one to follow * 8068 frees all 'hoarded' segments @@ -3833,6 +3833,13 @@ void Dbtc::execSIGNAL_DROPPED_REP(Signal * long signal buffering to store its sections */ jamEntry(); + + if (!assembleDroppedFragments(signal)) + { + jam(); + return; + } + const SignalDroppedRep* rep = (SignalDroppedRep*) &signal->theData[0]; Uint32 originalGSN= rep->originalGsn; @@ -3932,6 +3939,8 @@ void Dbtc::execSIGNAL_DROPPED_REP(Signal jam(); /* Don't expect dropped signals for other GSNs, * default handling + * TODO : Can TC get long TRANSID_AI as part of + * Unique index operations? */ SimulatedBlock::execSIGNAL_DROPPED_REP(signal); }; @@ -9463,6 +9472,48 @@ void Dbtc::systemErrorLab(Signal* signal }//Dbtc::systemErrorLab() +bool Dbtc::testFragmentDrop(Signal* signal) +{ + Uint32 fragIdToDrop= ~0; + /* Drop some fragments to test the dropped fragment handling code */ + if (ERROR_INSERTED(8074)) + fragIdToDrop= 1; + else if (ERROR_INSERTED(8075)) + fragIdToDrop= 2; + else if (ERROR_INSERTED(8076)) + fragIdToDrop= 3; + + if ((signal->header.m_fragmentInfo == fragIdToDrop) || + ERROR_INSERTED(8077)) // Drop all fragments + { + /* This signal fragment should be dropped + * Let's throw away the sections, and call the + * signal dropped report handler + * This code is replicating the effect of the code in + * TransporterCallback::deliver_signal() + */ + SectionHandle handle(this, signal); + Uint32 secCount= handle.m_cnt; + releaseSections(handle); + SignalDroppedRep* rep = (SignalDroppedRep*)signal->theData; + Uint32 gsn = signal->header.theVerId_signalNumber; + Uint32 len = signal->header.theLength; + Uint32 newLen= (len > 22 ? 22 : len); + memmove(rep->originalData, signal->theData, (4 * newLen)); + rep->originalGsn = gsn; + rep->originalLength = len; + rep->originalSectionCount = secCount; + signal->header.theVerId_signalNumber = GSN_SIGNAL_DROPPED_REP; + signal->header.theLength = newLen + 3; + signal->header.m_noOfSections = 0; + + EXECUTE_DIRECT(DBTC, GSN_SIGNAL_DROPPED_REP, signal, + newLen + 3); + return true; + } + return false; +} + /* ######################################################################### * * ####### SCAN MODULE ####### * * ######################################################################### * @@ -9559,6 +9610,19 @@ void Dbtc::execSCAN_TABREQ(Signal* signa { jamEntry(); + /* Test fragmented + dropped signal handling */ + if (ERROR_INSERTED(8074) || + ERROR_INSERTED(8075) || + ERROR_INSERTED(8076) || + ERROR_INSERTED(8077)) + { + jam(); + if (testFragmentDrop(signal)) { + jam(); + return; + } + } /* End of test fragmented + dropped signal handling */ + /* Reassemble if the request was fragmented */ if (!assembleFragments(signal)){ jam(); === modified file 'storage/ndb/src/kernel/vm/SimulatedBlock.cpp' --- storage/ndb/src/kernel/vm/SimulatedBlock.cpp 2008-11-19 11:01:17 +0000 +++ storage/ndb/src/kernel/vm/SimulatedBlock.cpp 2008-12-08 22:32:42 +0000 @@ -1758,6 +1758,9 @@ SimulatedBlock::execNDB_TAMPER(Signal * void SimulatedBlock::execSIGNAL_DROPPED_REP(Signal * signal){ + /* Note no need for fragmented signal handling as we are + * going to crash this node + */ char msg[64]; const SignalDroppedRep * const rep = (SignalDroppedRep *)&signal->theData[0]; BaseString::snprintf(msg, sizeof(msg), "%s GSN: %u (%u,%u)", getBlockName(number()), @@ -2019,6 +2022,8 @@ SimulatedBlock::assembleFragments(Signal fragPtr.p->m_sectionPtrI[sectionNo] = sectionPtr[i]; } + ndbassert(! fragPtr.p->isDropped() ); + /** * Don't release allocated segments */ @@ -2034,41 +2039,208 @@ SimulatedBlock::assembleFragments(Signal /** * FragInfo == 2 or 3 */ - Uint32 i; - for(i = 0; im_sectionPtrI[sectionNo] != RNIL){ - linkSegments(fragPtr.p->m_sectionPtrI[sectionNo], sectionPtrI); - } else { - fragPtr.p->m_sectionPtrI[sectionNo] = sectionPtrI; + if ( likely(! fragPtr.p->isDropped()) ) + { + Uint32 i; + for(i = 0; im_sectionPtrI[sectionNo] != RNIL){ + linkSegments(fragPtr.p->m_sectionPtrI[sectionNo], sectionPtrI); + } else { + fragPtr.p->m_sectionPtrI[sectionNo] = sectionPtrI; + } + } + + /** + * fragInfo = 2 + */ + if(fragInfo == 2){ + signal->header.m_fragmentInfo = 0; + signal->header.m_noOfSections = 0; + return false; + } + + /** + * fragInfo = 3 + */ + for(i = 0; i<3; i++){ + Uint32 ptrI = fragPtr.p->m_sectionPtrI[i]; + if(ptrI != RNIL){ + signal->m_sectionPtrI[i] = ptrI; + } else { + break; + } } + + signal->setLength(sigLen - secs); + signal->header.m_noOfSections = i; + signal->header.m_fragmentInfo = 0; + + c_fragmentInfoHash.release(fragPtr); + return true; + } + else + { + /* This fragmented signal has already had at least 1 fragment + * dropped. We must release the received segments. + */ + for (Uint32 i=0; i < secs; i++) + releaseSection( sectionPtr[i] ); + + signal->header.m_fragmentInfo = 0; + signal->header.m_noOfSections = 0; + + /* FragInfo == 2 + * More fragments to come, keep waiting + */ + if (fragInfo == 2) + return false; + + /* FragInfo == 3 + * That was the last fragment. + * We're now ready for handling the dropped signal. + */ + SignalDroppedRep * rep = (SignalDroppedRep*)signal->theData; + Uint32 gsn = signal->header.theVerId_signalNumber; + Uint32 len = signal->header.theLength; + Uint32 newLen= (len > 22 ? 22 : len); + memmove(rep->originalData, signal->theData, (4 * newLen)); + rep->originalGsn = gsn; + rep->originalLength = len; + rep->originalSectionCount = 0; + signal->header.theVerId_signalNumber = GSN_SIGNAL_DROPPED_REP; + signal->header.theLength = newLen + 3; + signal->header.m_noOfSections = 0; + signal->header.m_fragmentInfo = 3; + + + /* Perform dropped signal handling, in this thread, now */ + EXECUTE_DIRECT(theNumber, GSN_SIGNAL_DROPPED_REP, + signal, signal->header.theLength); + + /* return false to caller - they should not process the signal */ + return false; + } // else (isDropped()) + } + + /** + * Unable to find fragment + */ + ndbrequire(false); + return false; +} + +bool +SimulatedBlock::assembleDroppedFragments(Signal* signal) +{ + /* This method is called at the start of a SIGNAL_DROPPED_REP + * handler when there is a chance that the dropped signal could + * be part of a fragmented signal. + * If the dropped signal was a fragmented signal, this + * needs to be handled specially to ensure that fragments + * of the signal are correctly dropped to avoid segment + * leaks etc. + * There are a number of cases : + * 1) First fragment dropped (FragInfo=1) + * All remaining fragments must be dropped when they + * arrive. The Signal dropped report handler must be + * executed when the last fragment has arrived. + * 2) Middle fragment dropped (FragInfo=2) + * Any existing stored segments must be released. + * All remaining fragments must be dropped when they + * arrive. + * 3) Last fragment dropped (FragInfo=3) + * Any existing stored segments must be released. + * Signal Dropped handling can occur, so return true. + * + * To indicate that a fragment has been dropped for a signal, + * all the section I Values in the fragment's hash entry are + * set to RNIL. + * Signal Dropped Report handling is performed when the last + * fragment arrives. If the last fragment is not dropped + * by the transporter layer then normal fragment assembly + * arranges for dropped signal handling to occur. + */ + Uint32 sigLen = signal->length() - 1; + Uint32 fragId = signal->theData[sigLen]; + Uint32 fragInfo = signal->header.m_fragmentInfo; + Uint32 senderRef = signal->getSendersBlockRef(); + + if(fragInfo == 0){ + return true; + } + + /* This method is for handling SIGNAL_DROPPED_REP only */ + ndbrequire(signal->header.theVerId_signalNumber == GSN_SIGNAL_DROPPED_REP); + ndbrequire(signal->header.m_noOfSections == 0); + + if(fragInfo == 1){ + /** + * First in train + */ + Ptr fragPtr; + if(!c_fragmentInfoHash.seize(fragPtr)){ + ndbrequire(false); + return false; } + new (fragPtr.p)FragmentInfo(fragId, senderRef); + c_fragmentInfoHash.add(fragPtr); + + /* Mark entry in hash as belonging to dropped signal so subsequent + * fragments can also be dropped + */ + fragPtr.p->m_sectionPtrI[0]= RNIL; + fragPtr.p->m_sectionPtrI[1]= RNIL; + fragPtr.p->m_sectionPtrI[2]= RNIL; + + /* Wait for last fragment before SignalDroppedRep handling */ + signal->header.m_fragmentInfo = 0; + return false; + } + + FragmentInfo key(fragId, senderRef); + Ptr fragPtr; + if(c_fragmentInfoHash.find(fragPtr, key)){ + + /** + * FragInfo == 2 or 3 + */ + if (! fragPtr.p->isDropped() ) + { + /* Fragmented Signal not already marked as dropped + * Need to free stored segments + */ + releaseSection(fragPtr.p->m_sectionPtrI[0]); + releaseSection(fragPtr.p->m_sectionPtrI[1]); + releaseSection(fragPtr.p->m_sectionPtrI[2]); + + /* Mark as dropped now */ + fragPtr.p->m_sectionPtrI[0]= RNIL; + fragPtr.p->m_sectionPtrI[1]= RNIL; + fragPtr.p->m_sectionPtrI[2]= RNIL; + + ndbassert( fragPtr.p->isDropped() ); + } + /** * fragInfo = 2 + * Still waiting for final fragments. + * Return false to caller. */ if(fragInfo == 2){ signal->header.m_fragmentInfo = 0; - signal->header.m_noOfSections = 0; return false; } /** * fragInfo = 3 + * All fragments received, remove entry + * from hash and return to caller for + * dropped signal handling. */ - for(i = 0; i<3; i++){ - Uint32 ptrI = fragPtr.p->m_sectionPtrI[i]; - if(ptrI != RNIL){ - signal->m_sectionPtrI[i] = ptrI; - } else { - break; - } - } - - signal->setLength(sigLen - secs); - signal->header.m_noOfSections = i; signal->header.m_fragmentInfo = 0; c_fragmentInfoHash.release(fragPtr); === modified file 'storage/ndb/src/kernel/vm/SimulatedBlock.hpp' --- storage/ndb/src/kernel/vm/SimulatedBlock.hpp 2008-12-01 18:05:11 +0000 +++ storage/ndb/src/kernel/vm/SimulatedBlock.hpp 2008-12-08 15:17:42 +0000 @@ -319,6 +319,21 @@ protected: */ bool assembleFragments(Signal * signal); + /** + * Assemble dropped fragments + * + * Should be called at the start of a Dropped Signal Report + * (GSN_DROPPED_SIGNAL_REP) handler when it is expected that + * the block could receive fragmented signals. + * No dropped signal handling should be done until this method + * returns true. + * + * @return true if all fragments has arrived and dropped signal + * handling can proceed. + * false otherwise + */ + bool assembleDroppedFragments(Signal * signal); + /* If send size is > FRAGMENT_WORD_SIZE, fragments of this size * will be sent by the sendFragmentedSignal variants */ @@ -388,6 +403,13 @@ protected: inline Uint32 hashValue() const { return m_senderRef + m_fragmentId ; } + + inline bool isDropped() const { + /* IsDropped when entry in hash, but no segments stored */ + return (( m_sectionPtrI[0] == RNIL ) && + ( m_sectionPtrI[1] == RNIL ) && + ( m_sectionPtrI[2] == RNIL ) ); + } }; // sizeof() = 32 bytes /** === modified file 'storage/ndb/test/ndbapi/testLimits.cpp' --- storage/ndb/test/ndbapi/testLimits.cpp 2008-08-26 14:05:01 +0000 +++ storage/ndb/test/ndbapi/testLimits.cpp 2008-12-08 22:26:08 +0000 @@ -919,6 +919,95 @@ int testSegmentedSectionScan(NDBT_Contex return NDBT_OK; } +int testDropSignalFragments(NDBT_Context* ctx, NDBT_Step* step){ + /* Segmented section exhaustion results in dropped signals + * Fragmented signals split one logical signal over multiple + * physical signals (to cope with the MAX_SIGNAL_LENGTH=32kB + * limitation). + * This testcase checks that when individual signals comprising + * a fragmented signal (in this case SCANTABREQ) are dropped, the + * system behaves correctly. + * Correct behaviour is to behave in the same way as if the signal + * was not fragmented, and for SCANTABREQ, to return a temporary + * resource error. + */ + NdbRestarter restarter; + Ndb* pNdb= GETNDB(step); + + /* SEND > ((2 * MAX_SEND_MESSAGE_BYTESIZE) + SOME EXTRA) + * This way we get at least 3 fragments + * However, as this is generally > 64kB, it's too much AttrInfo for + * a ScanTabReq, so the 'success' case returns error 874 + */ + const Uint32 PROG_WORDS= 16500; + + struct SubCase + { + Uint32 errorInsertCode; + int expectedRc; + }; + const Uint32 numSubCases= 5; + const SubCase cases[numSubCases]= + /* Error insert Scanrc */ + {{ 0, 874}, // Normal, success which gives too much AI error + { 8074, 217}, // Drop first fragment -> error 217 + { 8075, 217}, // Drop middle fragment(s) -> error 217 + { 8076, 217}, // Drop last fragment -> error 217 + { 8077, 217}}; // Drop all fragments -> error 217 + const Uint32 numIterations= 50; + + Uint32 buff[ PROG_WORDS + 10 ]; // 10 extra for final 'return' etc. + + for (Uint32 iteration=0; iteration < (numIterations * numSubCases); iteration++) + { + /* Start a transaction */ + NdbTransaction* trans= pNdb->startTransaction(); + CHECKNOTNULL(trans); + + SubCase subcase= cases[iteration % numSubCases]; + + Uint32 errorInsertVal= subcase.errorInsertCode; + // printf("Inserting error : %u\n", errorInsertVal); + CHECKEQUAL(0, restarter.insertErrorInAllNodes(errorInsertVal)); + + NdbScanOperation* scan= trans->getNdbScanOperation(ctx->getTab()); + + CHECKNOTNULL(scan); + + CHECKEQUAL(0, scan->readTuples()); + + /* Create a large program, to give a large SCANTABREQ */ + NdbInterpretedCode prog(ctx->getTab(), buff, PROG_WORDS + 10); + + for (Uint32 w=0; w < PROG_WORDS; w++) + CHECKEQUAL(0, prog.load_const_null(1)); + + CHECKEQUAL(0, prog.interpret_exit_ok()); + CHECKEQUAL(0, prog.finalise()); + + CHECKEQUAL(0, scan->setInterpretedCode(&prog)); + + /* Api doesn't seem to wait for result of scan request */ + CHECKEQUAL(0, trans->execute(NdbTransaction::NoCommit)); + + CHECKEQUAL(0, trans->getNdbError().code); + + CHECKEQUAL(-1, scan->nextResult()); + + int expectedResult= subcase.expectedRc; + CHECKEQUAL(expectedResult, scan->getNdbError().code); + + scan->close(); + + trans->close(); + } + + restarter.insertErrorInAllNodes(0); + + return NDBT_OK; +} + + NDBT_TESTSUITE(testLimits); TESTCASE("ExhaustSegmentedSectionPk", @@ -935,6 +1024,11 @@ TESTCASE("ExhaustSegmentedSectionScan", INITIALIZER(testSegmentedSectionScan); } +TESTCASE("DropSignalFragments", + "Test behaviour of Segmented Section exhaustion with fragmented signals"){ + INITIALIZER(testDropSignalFragments); +} + NDBT_TESTSUITE_END(testLimits); int main(int argc, const char** argv){ === modified file 'storage/ndb/test/run-test/daily-basic-tests.txt' --- storage/ndb/test/run-test/daily-basic-tests.txt 2008-12-08 13:58:15 +0000 +++ storage/ndb/test/run-test/daily-basic-tests.txt 2008-12-08 22:28:27 +0000 @@ -1198,3 +1198,6 @@ max-time: 300 cmd: testMgm args: +max-time: 500 +cmd: testLimits +args: -n DropSignalFragments T1