Repository navigation
Refresh the engine once when a data update is applied in memory - #92
Draft
jwrosewell wants to merge 8 commits into
Draft
jwrosewell wants to merge 8 commits into
jwrosewell wants to merge 8 commits into
Conversation
DataUpdateService_CheckForUpdate_InMemory_RefreshesOnce runs a check for a data file with an update URL and no file path, so the downloaded data is applied in memory. It counts every refreshData call on the engine and expects exactly one, the call with the downloaded data.
When a data file has no file path, checkForUpdateFromUrl applies the downloaded data to the engine in memory, then returned AUTO_UPDATE_IN_PROGRESS. checkForUpdate took that to mean the engine had not been refreshed and called updatedFileAvailable, which refreshed the engine a second time through refreshData(String), the method that reloads from a data file. The in-memory path now returns AUTO_UPDATE_SUCCESS once the engine, or the configuration where there is no engine yet, holds the new data, as .NET's DataUpdateService does. checkForUpdate then clears the timer as updatedFileAvailable did, so the next check is registered as before.
…mer's check With a one second polling interval the timer's check can complete first, finding no update because the watcher is enabled, and release the test before the watcher has refreshed the engine. The failed run took 1.008 seconds. The flag is now released only by a completion whose status is AUTO_UPDATE_SUCCESS, which only the watcher's update gives.
The data updates page of the specification on rc says the automatic update master switch "only governs *automatic* activity - it does not disable *Programmatic update*". A programmatic update processes a newer file "as per newFile above", and the steps of newFileAvailable are: - pause file watcher, if any - pause update polling, if any - load file - resume polling, if any - resume file watcher, if any - trigger onUpdateCallbacks Three tests cover a manual check on a data file registered with the service, for each setting of automatic updates. DataUpdateService_CheckForUpdate_FileUpdated_TimerSetAgain applies an update from the data file, and DataUpdateService_CheckForUpdate_InMemory_TimerSetAgain applies one downloaded in memory. Each expects a new timer where automatic updates are on. Both are written to fail where automatic updates are on, because the manual update clears the timer and only a check the timer started registers the data file again. DataUpdateService_CheckForUpdate_NoUpdate_TimerKept checks that a manual check finding no update leaves the existing timer as it was. It passes before and after the change.
… says The data updates page of the specification on rc says the automatic update master switch "only governs *automatic* activity - it does not disable *Programmatic update*". Under Programmatic update, a newer file is processed "as per newFile above", and the steps of newFileAvailable include "resume polling, if any" and "resume file watcher, if any". checkForUpdate registered the data file again only after a check the timer started. A manual update cleared the timer, through updatedFileAvailable or the in-memory success branch, and set no new one, so polling stopped. checkForUpdate now also registers the data file again after a manual check that applied an update, where the data file is registered with the service, so registerDataFile sets the next timer when automatic updates are on, as it does after an automatic check. A manual check that finds no update still leaves the timer as it was, and a data file that was never registered is not registered by a manual check.
Both TimerSetAgain tests now keep the timer pending before the manual update and check, where automatic updates are on, that it was cancelled and that the data file holds a different one. With the existing check that exactly one more timer was scheduled, this shows a manual update with a timer pending leaves one live timer. The pending timer is cancelled and cleared under futureLock, by updatedFileAvailable for the file path or by the in-memory success branch, before registerDataFile schedules a new one, and registerDataFile schedules only while the future is null under the same lock.
…ng unregisters DataUpdateService_CheckForUpdate_FileRefreshFails_TimerSetAgain makes a manual check whose refresh from the data file fails, on a data file registered for automatic updates, and expects the next update timer to be set where automatic updates are on. The newFileAvailable steps in the data updates page of the specification say "resume polling, if any", with no exception for a failed load. DataUpdateService_Unregister_NoLongerRegistered expects a data file to be no longer registered after unregisterDataFile, so a manual update of it sets no timer. Both are written to fail on the previous commit. The first fails where automatic updates are on.
…ion on unregister updatedFileAvailable clears the update timer before it refreshes the engine, so after a manual check whose refresh failed polling stopped, where an automatic check registers the data file again. checkForUpdate now also registers a registered data file again after a manual check that returned AUTO_UPDATE_REFRESH_FAILED. A manual failure that did not clear the timer, such as one in memory, keeps the timer it had, because registerDataFile schedules only while there is none. So a manual check leaves one timer whether it applied an update, failed or found none. unregisterDataFile now sets the data file's update service to null, so getIsRegistered is false after it and a later manual update does not register the data file again.
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
When a data file has an update URL and no file path, the data update service applies a downloaded update in memory. On
rcat b92cfcf,checkForUpdateFromUrlcallsrefreshData(identifier, data)on the engine with the downloaded data, or puts the data in the configuration where there is no engine yet. It then returnsmemoryDownloadResult, which is alwaysAUTO_UPDATE_IN_PROGRESSat that point.checkForUpdatereadsAUTO_UPDATE_IN_PROGRESSas "an update is available but the engine has not been refreshed". It therefore callsupdatedFileAvailable, which callsrefreshData(identifier)on the same engine. That is a second refresh, through the method that reloads from a data file, when the update was applied in memory because there is no data file path. The interface documentation ofrefreshData(String)says it reloads from the file atgetDataFilePath(), and that an engine built from a byte[] should be refreshed through the byte[] overload instead. If the second refresh throws,updatedFileAvailableretries it up to 10 times and the check reportsAUTO_UPDATE_REFRESH_FAILED, although the first refresh had applied the update.The existing test
DataUpdateService_Register_UpdateOnStartup_InMemorychecks only the call with the downloaded data, so it did not catch the second call.What .NET does
On the same path, .NET's
CheckForUpdateFromUrlsets the result toAUTO_UPDATE_SUCCESSonceRefreshData(identifier, stream)succeeds (line 961), or once the stream is put in the configuration where there is no engine (line 978).CheckForUpdatecallsUpdatedDataAvailableonly forAUTO_UPDATE_IN_PROGRESS(line 855). OnAUTO_UPDATE_SUCCESSit clears the timer and registers the data file again (lines 862 and 865). So .NET refreshes the engine once.What changed
checkForUpdateFromUrlnow returnsAUTO_UPDATE_SUCCESSonce the engine, or the configuration, holds the new data, as .NET does.checkForUpdatehas a new branch forAUTO_UPDATE_SUCCESS, which clears the timer the same wayupdatedFileAvailabledid on this path.registerDataFilethen registers the next check.checkForUpdateFromUrlnever returnedAUTO_UPDATE_SUCCESSbefore, so the new branch is only reached from the in-memory path.Polling after a manual update
The data updates page on
rcsays the following in three places.Before,
checkForUpdateregistered the data file again only after a check the timer started. A manual update cleared the timer, throughupdatedFileAvailableon the file path or the in-memory success branch, and set no new one, so polling stopped after it.checkForUpdatenow also registers the data file again after a manual check that applied an update, where the data file is registered with the service. SoregisterDataFilesets the next timer when automatic updates are on, as it does after an automatic check. This covers both the file path and the in-memory path.futureLock, byupdatedFileAvailable(line 294) or by the in-memory success branch (line 232), beforeregisterDataFileruns.registerDataFileschedules a timer only while the future is null, under the same lock (lines 448 to 450).DataUpdateService_CheckForUpdate_FileUpdated, which makes a manual check on an unregistered data file and expects no timer, as it was.CheckForUpdatedoes (DataUpdateService.cs line 356 at 5b4d06e), and that is being changed to match the master switch rule above.Overlap with other drafts
Two open drafts also edit
DataUpdateServiceDefault.java. This change stays off their lines, andgit merge-treeshows it merges cleanly with each of their heads.checkForUpdateFromUrlcall incheckForUpdate(old line 218), thedownloadFilecall and the log lines in the in-memory path (old lines 792, 797 to 799 and 810 to 812), and the retry loop inupdatedFileAvailable. This change edits old line 808, adds a branch after old line 228, and changes the re-registration condition at old line 252, which Make the data update follow the specification #79 does not edit. Between them sit unchanged lines (old line 809, and old lines 219 to 228). Make the data update follow the specification #79 also editsDataUpdateServiceTests.javaat old lines 371, 884 and 1391, and the new test sits after old line 1222.downloadFile(old lines 615 to 671) only.For the release notes
refreshData(String), and a failure of that second refresh was reported as a failed update.How to verify
rc(b92cfcf)DataUpdateServiceTestsrc's code.DataUpdateService_CheckForUpdate_InMemory_RefreshesOncemakes a manual check for a data file with an update URL and no file path, and counts everyrefreshDatacall on the mocked engine. It is written to fail there in all 4 parameter sets, finding two calls,refreshData(String, byte[])and thenrefreshData(String).DataUpdateService_Register_UpdateOnStartup_InMemoryshould still pass, as it still sees one call with the downloaded data and one timer scheduled.DataUpdateService_CheckForUpdate_FileUpdated_TimerSetAgainapplies an update from the data file.DataUpdateService_CheckForUpdate_InMemory_TimerSetAgainapplies one downloaded in memory.DataUpdateService_CheckForUpdate_NoUpdate_TimerKeptchecks that a manual check finding no update keeps the same timer and schedules none. It passes before and after the change.None of these commits has been built locally. The results of the CI runs are to be added here.