Repository navigation
Register the data file again after a refresh from a watched file, so polling carries on - #98
Merged
Merged
Conversation
A new data file found by the file watcher refreshes the engine, and then polling for updates must carry on, as the newFileAvailable steps of the data update specification say. The test runs the watcher's task itself and records each polling timer the service schedules. After the refresh there must be a second timer, held by the data file.
updatedFileAvailable cancels the polling timer before it refreshes the engine. checkForUpdate registers the data file again afterwards, which sets a new timer, but dataFileUpdated, which the file watcher calls, did not, so HTTP polling stopped after the first refresh from a watched file. The newFileAvailable steps of the data update specification say to resume polling after loading the file and before the callbacks. dataFileUpdated now registers a registered data file again after the refresh and before the callbacks. Registering a file that is already registered only sets the timer, because the watcher and the start-up check are each set up once. The comment on updatedFileAvailable said it registered the file itself, which it does not, and now says the caller does.
On Windows the full build failed in the watched file test's clean up, with "The process cannot access the file because it is being used by another process". The data update service reads the file through BinaryReader, which maps it, and Windows keeps a mapped file until the buffer is collected. The test's assertions had passed. The clean up now deletes what it can and leaves the rest to be deleted when the test run ends, as the existing watcher test does by ignoring a failed delete.
…ter-watched-refresh
The data update specification's Errors table says a watched file that cannot be opened for reading within 10 minutes ends the update with AUTO_UPDATE_TIMEOUT and logs nothing, as .NET does. Java tries for ever with no pause, so the new test, which shortens the wait to one second, never sees the update end. This commit adds the AUTO_UPDATE_TIMEOUT status, the ten minute wait and the half second pause as constants, and the field the test shortens, so the test compiles and fails on behaviour. The loop does not use them yet. The setup the two watched file tests share moves into helpers.
The file watcher tried to open a new data file for ever, with no pause and a warning on every try, while holding the data file's update lock. It now tries every half second for up to ten minutes, logs nothing, and then ends the update with AUTO_UPDATE_TIMEOUT, leaving the engine and the polling timer as they were. This is what .NET does and what the Errors table of the data update specification says. The file is registered again only when the refresh ran, because only the refresh cancels the polling timer.
…finished An interrupt, as unregistering the data file gives the watcher's task, ends the wait for a file that cannot be opened, and the update reports AUTO_UPDATE_TIMEOUT although the file was not tried for the whole wait. The new test requires AUTO_UPDATE_IN_PROGRESS, the status for an update that did not finish, so it fails on the timeout. The two tests of a file that cannot be opened now share one fixture. The stand-in file pauses without sleeping, so an interrupt reaches the service rather than the stand-in.
An interrupt ended the wait and reported AUTO_UPDATE_TIMEOUT, although the file had not been tried for the whole wait. The update now keeps the status it started with, AUTO_UPDATE_IN_PROGRESS, which is what it reports for any watcher event that does not finish an update. The engine and the polling timer are left as they were, as for a timeout. The IOException the loop catches is no longer named e, as nothing reads it.
jwrosewell
marked this pull request as ready for review
November 11, 2026 10:34
This was referenced Nov 11, 2026
Draft
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.
The problem
This draft fixes two faults in the file watcher path of the data update service.
The cause
At
rcd39b6af,updatedFileAvailablecancels the polling timer and sets it to null (lines 304 to 310) before it refreshes the engine. Two methods call it.checkForUpdate, the polling path, callsregisterDataFileafterwards, andregisterDataFilesets a new timer because the old one is null (lines 458 to 495).dataFileUpdated, which the file watcher calls, does not. The timer stays null, so polling ends.The wait is the
while (fileLockable == false)loop indataFileUpdated(lines 374 to 385 at d39b6af), which only ends when the file opens.What the specification says
specifications#60 merged into specifications
rcat 07:37 UTC today. Atrce89679e, the newFileAvailable steps of the data update page cover a file from the watcher as well as a download.The Update polling section adds "Once the refresh is complete, the data update service can start checking for updates using the newly refreshed next expected update date/time."
The Errors table has this row, following .NET's messages, levels and statuses.
Reporting each check gives
AUTO_UPDATE_IN_PROGRESS"for a file watcher event that found nothing left to do". The page does not cover a wait that is interrupted, because .NET's wait cannot be.What .NET does
On 51degrees-dotnet
rcat b3e90b4,UpdatedDataAvailablerefreshes the engine and leaves the timer alone. OnlyCheckForUpdateclears the timer and registers the data file again, after a successful refresh. So a refresh from the watched file leaves .NET's polling running. Java cancels the timer on both paths, so it has to register again on both.In
DataFileUpdatedInternal, .NET tries to open the file untilDateTime.UtcNow.AddMinutes(10), waits 500 ms after each failed try, logs nothing, and then reportsAUTO_UPDATE_TIMEOUTwithout refreshing the engine. Its status reads "An operation could not be completed within the expected time."What changed
Head 3439e5e. Links are at that commit.
dataFileUpdatedregisters a registered data file again after the refresh and before the callbacks, the order the specification gives. Registering a file that is already registered only sets the timer, becauseregisterDataFilesets up the watcher only while there is no watch key, and runs the start-up check only for a file not yet registered. The new timer uses the next expected update the refresh set, as the Update polling section says. The file is registered again after a refresh that failed as well, ascheckForUpdatedoes for a polled check, so polling carries on either way.AUTO_UPDATE_TIMEOUTstatus. The engine is not refreshed then, and the polling timer is left as it was, because only a refresh cancels it, so the file is registered again only when the refresh ran. The same file is not tried again, because its time was already recorded, and a file written later is. The exception the loop catches is namedignored, as nothing reads it, which is as near as Java comes to .NET'scatch (IOException).cancel(true), ends the wait at once. The file was not tried for the whole wait, so the update does not report a timeout. It keeps the status it started with,AUTO_UPDATE_IN_PROGRESS, the status for a watcher event that did not finish an update, and the engine and the polling timer are left as they were. The specification leaves this case open, as above.AUTO_UPDATE_TIMEOUTis added at the end ofAutoUpdateStatus, with .NET's text, so no existing value changes its place.updatedFileAvailablegives its steps in order and says the caller registers a registered data file again. It used to say the method itself did that, which it does not.How it was checked
DataUpdateServiceWatchedFileTestsholds three tests. Each records the polling timers the service schedules and runs the watcher's task itself, on a real watch of a temporary folder. Like the existing watcher test, they do not run on macOS, whose file watcher is not reliable.WatchedFileRefresh_PollingCarriesOnchanges the file, then requires the engine to be refreshed once, a second polling timer to be scheduled, and the data file to hold it.WatchedFileCannotBeOpened_GivesUpAfterTheWaituses a file that throws on every open. It requires the update to end within 10 seconds withAUTO_UPDATE_TIMEOUT, no more than four tries in the one second wait, no warning or error logged, no refresh, and the first polling timer kept.WatchedFileWaitInterrupted_LeavesTheUpdateUnfinisheduses the same file with a 60 second wait, interrupts the watcher's thread once the file has been tried, and requires the update to end within 5 seconds withAUTO_UPDATE_IN_PROGRESS, nothing logged, no refresh and the first polling timer kept.The last two share one fixture. Its stand-ins for the file and the logger keep no record of their calls, so a loop that never ends cannot fill the memory, and the stand-in file pauses without sleeping, so an interrupt reaches the service.
Each run used a temporary workflow on a
tmp/branch that builds the named modules and runs the named tests. The polling runs were on Ubuntu with Java 17, and the wait runs ran every data update service test, 137 and then 138 of them, on Ubuntu and Windows with Java 8 and 21.rcWatchedFileWaitInterrupted_LeavesTheUpdateUnfinished"expected:<AUTO_UPDATE_IN_PROGRESS> but was:<AUTO_UPDATE_TIMEOUT>", 1 of 138 in each of the 4 jobsThe Windows jobs of the green run also cover 0beb384, which leaves to
deleteOnExita data file Windows still holds, because the reader maps the file and Windows keeps a mapped file until the buffer is collected. The three tests share that clean up.The full build's 9 jobs without macOS all passed, being Ubuntu Java 8, 11, 17 and 21, Ubuntu ARM Java 17, and Windows Java 8, 11, 17 and 21. Every
Setting 'PullRequestSha'line, in Configure and in each of the 9 jobs, is 3439e5e, the head, and Configure printed "Left out 6 macOS entries". Two earlier full builds are superseded, 42904274257 at 0beb384, before the bound, and 42981459548 at 03999c5, whose first attempt failed on Ubuntu Java 8 when Maven could not read the descriptor ofplexus-compiler-manager2.17.0, and whose rerun was cancelled when the head moved.macOS was left out of this draft's full build under the 9 November decision. The rc head is built with macOS before it merges into main.
The branch has
rcat c77fb05 merged in, which movescxxto c6d4e4d9.Other drafts
#79 changes
DataUpdateServiceDefault.javatoo, incheckForUpdate,updatedFileAvailable,updateFromMemory,registerDataFileand the download. This draft changes the comment aboveupdatedFileAvailable, the constants at the start of the class anddataFileUpdated, and #79 changes neitherdataFileUpdatednorAutoUpdateStatus.git merge-tree --write-treeof this draft's head with #79 at 10a8a5d reports no conflicts.What remains
Errorthrown byrefreshDataleavescheckForUpdatebefore it registers the file again. That is left for later.AUTO_UPDATE_IN_PROGRESSwhere .NET and the newFileAvailable section reportAUTO_UPDATE_NOT_NEEDED.AutoUpdateStatusstill lacks three statuses .NET has and the Errors table names, beingAUTO_UPDATE_NO_CONFIGURATION,AUTO_UPDATE_TEMP_PATH_NOT_SETandAUTO_UPDATE_UNKNOWN_ERROR. Points 2 and 3 belong with the work to align Java's update messages and statuses with .NET.