Conversation
… before the DoneMsg as a failed import A total update into this replica whose broker was stopped before the DoneMsg of the exporter arrived ended as a finished import: receiveEntryBytes() returned null with no exception recorded when the broker was shutting down, ReplInputStream turned that into the end of stream a DoneMsg produces, and importBackend() kept the entries which had arrived under the generationId of the exporter, loaded from the base entry among them. The task completed as a success, and the replica came up as a peer of the exporter over part of its data. The stop is now recorded as the exception of the import, whichever road stopped the broker - the shutdown of the server, or a restart of the session, which every road takes through disableService() - so the import is reported as failed and its generationId is computed over the data, as it is for every other failed import. InitOnLineTest.initializeImportSessionStoppedBeforeDone drives the total update from a broker of the test, stops the session between two EntryMsgs, and asserts that the task fails for that reason and that neither the domain nor the base entry keeps the generationId of the exporter.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A total update into this replica whose broker is stopped before the exporter's
DoneMsgarrives ends as a finished import.receiveEntryBytes()reads a broker which is shutting down as the end of the stream and records nothing:ReplInputStreamturns thatnullinto the same-1aDoneMsgproduces, soimportBackend()closes the import on the entries which had arrived,loadDataState()loads the exporter's ServerState and generationId from the base entry among them -ds-sync-stateandds-sync-generation-idare operational attributes, exported with the base entry, which comes first - andinitialize()logsNOTE_FULL_UPDATE_ENGAGED_FROM_REMOTE_ENDwith an empty cause and completes the task as a success. The replica then comes up as a peer of the exporter over part of its data: the changes it is missing are covered by the ServerState it loaded, and the exporter'swaitForRemoteEndOfInit()reads the matching generationId as a success too.The comment reads the stop as the shutdown of the server, and
broker.stop()is reached on more roads than that: every restart of the session goes throughdisableService()- the restart a failed replay asks for (#968 keeps it out of an import; #1041 is the window which remains), the restart a configuration change asks for (#1040) - and a total update into this replica never setsdisabled, so none of the owner guards sees it.What this changes
receiveEntryBytes()records the stop as the exception of the import, whichever road stopped the broker: a newERR_INIT_SESSION_STOPPED_DURING_IMPORT, next to theERR_INIT_RS_DISCONNECTION_DURING_IMPORTa lost connection already records. Everything after it is what every other failed import already does:importBackend()computes the generationId over the data and stores it in place of the exporter's,initialize()makes no new attempt on a broker which is not connected, logs the end note with the cause, and completes the task asSTOPPED_BY_ERRORwith it.On the shutdown road this adds the checksum export of the partial data to the cleanup the listener thread runs while
disableService()joins it - a cost proportional to what arrived. The alternative, leaving the generationId unstored for the next start to compute, is not correct for the restart roads:enableService()does not reload the state, and the session would come back up with the exporter's generationId still in memory.Not in this change
countEntryLimits()counts"\n\n"inside eachEntryMsg, and a real export sends 8192-byte slices of the LDIF stream (BufferedOutputStreamoverReplOutputStream), so a pair split by a slice boundary is missed on both sides; it looks for'\n','\n'while the exporter writes its platform's line separator, so the count is 0 from a Windows exporter; and the announced count isgetNumberOfEntriesInBaseDN()at the start of an export the replay keeps running through. A comparison would report complete imports as cut. The exact signal is theDoneMsgitself, and after this change everyreturn nullofreceiveEntryBytes()is either theDoneMsgor an exception recorded, so a "no DoneMsg and no exception" guard would be unreachable.ErrorMsgininitialize()needs a connected broker, and the broker is stopped. If the importer's session comes back before the replication server reports the gap, the exporter keeps streaming into a listener which trashesEntryMsgs, and waits for acknowledgements which will not come inexportLDIFEntry(), with its import/export context held. Pre-existing on the exporter's side, and not verified by a run here; it deserves an issue of its own.Tests
InitOnLineTest.initializeImportSessionStoppedBeforeDone: the total update asked for by a task on this server, a broker of the test as the exporter, the base entry sent withds-sync-generation-id: 7777777the way the base entry of a real export carries the exporter's generationId, one more entry, noDoneMsg; once the task reports two entries left,disableService()- the primitive every road to a stopped session goes through. It asserts the task endsSTOPPED_BY_ERRORwith two entries left and two done, that its log names the stopped session as the reason, and that neithergetGenerationID()nords-sync-generation-idon the base entry is the exporter's.Without the fix it fails on the first assertion:
expected [STOPPED_BY_ERROR] but found [COMPLETED_SUCCESSFULLY]. A mutant which records the lost-connection message on the stopped broker instead fails on the reason.InitOnLineTestis 11/11 undermvn -Pprecommit verify -Dit.test=InitOnLineTest, andGenerationIdTest4/4,ReSyncTest2/2,UpdateOperationTest31/31,AssuredReplicationPluginTest14/14,StateWithoutBaseEntryTest3/3 andReplicationDomainTest12/12 on the same tree, one JVM per class, nothing skipped.Ordinal
ERR_INIT_SESSION_STOPPED_DURING_IMPORT_329: master holds up to 327, and the open PRs which add toreplication.propertieshold 318, 322-325 and 328 (#1019).Fixes #1039