/** * @brief: random test 2, unoverlapped range lock and unlock * */ void random_test2(){ int i, j; for(j = 0; j < 2; j++){ treeInit(); /* *to check the lock */ for(i = 0; i < MAX_NODES; i++){ unsigned start_lba = i*5; unsigned end_lba = start_lba + 4; unsigned type = rand()%2; lockRequest(start_lba, end_lba, type, 1, 0); } //treeDump(rootArray[0]); /* *to check the unlock */ for(i = 0; i < MAX_NODES -2; i++){ printf("\n\ndeleting case %d with", i+ 1); int index = (MAX_NODES - i - 1); printf(" event index %d\n", nodes[index].eventIndex); lockRelease(&nodes[index], 0); } treeDump(rootArray[0]); /* *to check the index overflow */ for(i = 0; i < MAX_NODES; i++){ unsigned start_lba = i*5; unsigned end_lba = start_lba + 4; unsigned type = rand()%2; lockRequest(start_lba, end_lba, type, 1, 0); treeDump(rootArray[0]); } printf("\n"); } }
/*********************************************************************************************************************************** Get an archive file from the repository (WAL segment, history file, etc.) ***********************************************************************************************************************************/ int cmdArchiveGet(void) { FUNCTION_LOG_VOID(logLevelDebug); // Set the result assuming the archive file will not be found int result = 1; MEM_CONTEXT_TEMP_BEGIN() { // Check the parameters const StringList *commandParam = cfgCommandParam(); if (strLstSize(commandParam) != 2) { if (strLstSize(commandParam) == 0) THROW(ParamRequiredError, "WAL segment to get required"); if (strLstSize(commandParam) == 1) THROW(ParamRequiredError, "path to copy WAL segment required"); THROW(ParamInvalidError, "extra parameters found"); } // Get the segment name String *walSegment = strBase(strLstGet(commandParam, 0)); // Destination is wherever we were told to move the WAL segment const String *walDestination = walPath(strLstGet(commandParam, 1), cfgOptionStr(cfgOptPgPath), STR(cfgCommandName(cfgCommand()))); // Async get can only be performed on WAL segments, history or other files must use synchronous mode if (cfgOptionBool(cfgOptArchiveAsync) && walIsSegment(walSegment)) { bool found = false; // Has the WAL segment been found yet? bool queueFull = false; // Is the queue half or more full? bool forked = false; // Has the async process been forked yet? bool confessOnError = false; // Should we confess errors? // Loop and wait for the WAL segment to be pushed Wait *wait = waitNew((TimeMSec)(cfgOptionDbl(cfgOptArchiveTimeout) * MSEC_PER_SEC)); do { // Check for errors or missing files. For archive-get ok indicates that the process succeeded but there is no WAL // file to download. if (archiveAsyncStatus(archiveModeGet, walSegment, confessOnError)) { storageRemoveP( storageSpoolWrite(), strNewFmt(STORAGE_SPOOL_ARCHIVE_IN "/%s" STATUS_EXT_OK, strPtr(walSegment)), .errorOnMissing = true); break; } // Check if the WAL segment is already in the queue found = storageExistsNP(storageSpool(), strNewFmt(STORAGE_SPOOL_ARCHIVE_IN "/%s", strPtr(walSegment))); // If found then move the WAL segment to the destination directory if (found) { // Source is the WAL segment in the spool queue StorageFileRead *source = storageNewReadNP( storageSpool(), strNewFmt(STORAGE_SPOOL_ARCHIVE_IN "/%s", strPtr(walSegment))); // A move will be attempted but if the spool queue and the WAL path are on different file systems then a copy // will be performed instead. // // It looks scary that we are disabling syncs and atomicity (in case we need to copy intead of move) but this // is safe because if the system crashes Postgres will not try to reuse a restored WAL segment but will instead // request it again using the restore_command. In the case of a move this hardly matters since path syncs are // cheap but if a copy is required we could save a lot of writes. StorageFileWrite *destination = storageNewWriteP( storageLocalWrite(), walDestination, .noCreatePath = true, .noSyncFile = true, .noSyncPath = true, .noAtomic = true); // Move (or copy if required) the file storageMoveNP(storageSpoolWrite(), source, destination); // Return success result = 0; // Get a list of WAL segments left in the queue StringList *queue = storageListP( storageSpool(), STORAGE_SPOOL_ARCHIVE_IN_STR, .expression = WAL_SEGMENT_REGEXP_STR); if (strLstSize(queue) > 0) { // Get size of the WAL segment uint64_t walSegmentSize = storageInfoNP(storageLocal(), walDestination).size; // Use WAL segment size to estimate queue size and determine if the async process should be launched queueFull = strLstSize(queue) * walSegmentSize > cfgOptionUInt64(cfgOptArchiveGetQueueMax) / 2; } } // If the WAL segment has not already been found then start the async process to get it. There's no point in // forking the async process off more than once so track that as well. Use an archive lock to prevent forking if // the async process was launched by another process. if (!forked && (!found || !queueFull) && lockAcquire(cfgOptionStr(cfgOptLockPath), cfgOptionStr(cfgOptStanza), cfgLockType(), 0, false)) { // Get control info PgControl pgControl = pgControlFromFile(cfgOptionStr(cfgOptPgPath)); // Create the queue storagePathCreateNP(storageSpoolWrite(), STORAGE_SPOOL_ARCHIVE_IN_STR); // The async process should not output on the console at all KeyValue *optionReplace = kvNew(); kvPut(optionReplace, VARSTR(CFGOPT_LOG_LEVEL_CONSOLE_STR), VARSTRDEF("off")); kvPut(optionReplace, VARSTR(CFGOPT_LOG_LEVEL_STDERR_STR), VARSTRDEF("off")); // Generate command options StringList *commandExec = cfgExecParam(cfgCmdArchiveGetAsync, optionReplace); strLstInsert(commandExec, 0, cfgExe()); // Clean the current queue using the list of WAL that we ideally want in the queue. queueNeed() // will return the list of WAL needed to fill the queue and this will be passed to the async process. const StringList *queue = queueNeed( walSegment, found, cfgOptionUInt64(cfgOptArchiveGetQueueMax), pgControl.walSegmentSize, pgControl.version); for (unsigned int queueIdx = 0; queueIdx < strLstSize(queue); queueIdx++) strLstAdd(commandExec, strLstGet(queue, queueIdx)); // Release the lock so the child process can acquire it lockRelease(true); // Fork off the async process if (forkSafe() == 0) { // Disable logging and close log file logClose(); // Detach from parent process forkDetach(); // Execute the binary. This statement will not return if it is successful. THROW_ON_SYS_ERROR( execvp(strPtr(cfgExe()), (char ** const)strLstPtr(commandExec)) == -1, ExecuteError, "unable to execute '" CFGCMD_ARCHIVE_GET_ASYNC "'"); } // Mark the async process as forked so it doesn't get forked again. A single run of the async process should be // enough to do the job, running it again won't help anything. forked = true; } // Exit loop if WAL was found if (found) break; // Now that the async process has been launched, confess any errors that are found confessOnError = true; } while (waitMore(wait)); } // Else perform synchronous get else {
/*********************************************************************************************************************************** Do cleanup and return result code ***********************************************************************************************************************************/ int exitSafe(int result, bool error, SignalType signalType) { FUNCTION_LOG_BEGIN(logLevelDebug); FUNCTION_LOG_PARAM(INT, result); FUNCTION_LOG_PARAM(BOOL, error); FUNCTION_LOG_PARAM(ENUM, signalType); FUNCTION_LOG_END(); // Report error if one was thrown if (error) { // Don't log the error if it has already been logged by Perl #ifdef HAVE_LIBPERL if (strcmp(errorMessage(), PERL_EMBED_ERROR) != 0) { #endif LogLevel logLevel = errorCode() == errorTypeCode(&AssertError) ? logLevelAssert : logLevelError; // Assert errors always output a stack trace if (logLevel == logLevelAssert) LOG(logLevel, errorCode(), "%s\nSTACK TRACE:\n%s", errorMessage(), errorStackTrace()); else { // Log just the error to non-debug levels LOG_INTERNAL(logLevel, LOG_LEVEL_MIN, logLevelDetail, 0, errorCode(), errorMessage()); // Log the stack trace debug levels if (logAny(logLevelDebug)) { LOG_INTERNAL( logLevel, logLevelDebug, LOG_LEVEL_MAX, 0, errorCode(), "%s\nSTACK TRACE:\n%s", errorMessage(), errorStackTrace()); } } #ifdef HAVE_LIBPERL } #endif result = errorCode(); } // Free protocol objects but ignore errors TRY_BEGIN() { protocolFree(); } TRY_END(); // Free Perl but ignore errors #ifdef HAVE_LIBPERL TRY_BEGIN() { perlFree(result); } TRY_END(); #endif // Log command end if a command is set if (cfgCommand() != cfgCmdNone) { String *errorMessage = NULL; // On error generate an error message if (result != 0) { // On process terminate if (result == errorTypeCode(&TermError)) { errorMessage = strNew("terminated on signal "); // Terminate from a child if (signalType == signalTypeNone) strCat(errorMessage, "from child process"); // Else terminated directly else strCatFmt(errorMessage, "[SIG%s]", exitSignalName(signalType)); } // Standard error exit message else if (error) errorMessage = strNewFmt("aborted with exception [%03d]", result); } cmdEnd(result, errorMessage); } // Release any locks but ignore errors TRY_BEGIN() { lockRelease(false); } TRY_END(); // Return result - caller should immediate pass this result to exit() FUNCTION_LOG_RETURN(INT, result); }
/** * @brief: Test one example */ void test_case1(){ treeInit(); tree_node_t *n1,*n2, *n3, *n4, *n5, *n6; n1 = allocNodes(); n2 = allocNodes(); n3 = allocNodes(); n4 = allocNodes(); n5 = allocNodes(); n6 = allocNodes(); /** * Event 1: R [ 1- 40] */ n1->eventIndex = 1; n1->start_lba = 1; n1->end_lba = 40; n1->type = 0; /** * Event 2: W [ 1- 10] */ n2->eventIndex = 2; n2->start_lba = 1; n2->end_lba = 10; n2->type = 1; /** * Event 3: W [ 8- 20] */ n3->eventIndex = 3; n3->start_lba = 8; n3->end_lba = 20; n3->type = 1; /** * Event 4: W [ 21- 30] */ n4->eventIndex = 4; n4->start_lba = 21; n4->end_lba = 30; n4->type = 1; /** * Event 5: W [ 31- 40] */ n5->eventIndex = 5; n5->start_lba = 31; n5->end_lba = 40; n5->type = 1; /** * Event 6: R [ 1- 40] */ n6->eventIndex = 6; n6->start_lba = 1; n6->end_lba = 40; n6->type = 0; /*insert all events, it should be a linked list * event 1 R[1-40] -->event 2 W[1-10] -->event 3 W[8-20] -->event 4 W[21-30] -->event 5 W[31-40] -->event 6 R[1-40] */ insertNode(&rootArray[0], n1, 1); insertNode(&rootArray[0], n2, 1); insertNode(&rootArray[0], n3, 1); insertNode(&rootArray[0], n4, 1); insertNode(&rootArray[0], n5, 1); insertNode(&rootArray[0], n6, 1); treeDump(rootArray[0]); printf("remove n1\n"); lockRelease(n1, 0); /* * the avl tree should be: * event 4 --> event 6 * / \ *event 3 <- event 2 event 5 */ treeDump(rootArray[0]); /** * delete event 6, and it should be rejected. */ printf("----\n\n"); printf("remove n6\n"); lockRelease(n6, 0); treeDump(rootArray[0]); /* * delete event 2, and the AVL tree should be like this * * event 4 --> event 3 -->event 6 * \ * event 5 */ printf("---\n\n"); printf("remove n2\n"); lockRelease(n2, 0); treeDump(rootArray[0]); /* * delete event 4, and the AVL tree should be like this * * event 5 ->event 6 * / * event 3 */ printf("--\n\n"); printf("remove n4\n"); lockRelease(n4, 0); treeDump(rootArray[0]); /* * delete event 5, and the AVL tree should be like this * * event 3 -->event 6 * */ printf("---\n\n"); printf("remove n5\n"); lockRelease(n5, 0); treeDump(rootArray[0]); /* * delete event 3, and the AVL tree should be like this. * * event 6 */ printf("--\n\n"); printf("remove n3\n"); lockRelease(n3, 0); treeDump(rootArray[0]); }