New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Renaming and recreating the project directory causes Gradle to lose track of changes on Windows #17865
Comments
One thing we could easily do to prevent this specific problem of Swabra renaming the watched directory is to prevent renaming. We can do it by removing This is what IDEs typically do, but it also means that Gradle daemons would need to be killed before their watched directories could be deleted. As we can't observe the rename, I'm not sure what else we could do. |
We could also reach out to JetBrains to make Swabra delete the checkout directory in a better way. |
Yet another option, just to have everything on the table, is to somehow not enable FSW on CI by default. This would obviously require Gradle to be aware somehow whether we are running on CI or not. |
There's another thing we can do: we could (perhaps at the start of the build, or every ten minutes etc.) check if our watchers still point to where they are supposed to. We keep hold of a file handle that we can use to retrieve the full path of the watched directory, and match it against the registered location. We could do something like I wonder how useful this would be for other operating systems. Probably not very. |
We could also go crazy, and check the current location of the watched directory against the registered path every time we receive notifications. This would slow things down to some degree, though probably not in any significant way. It's a less leaky solution than checking the location at arbitrary times, and it does not require changing the |
This has the problem of not detecting if somebody only moves the project directory, but doesn't delete/modify any files in it. Gradle would keep thinking that it watches the location of the project directory, but it actually doesn't. |
It looks like other file watchers like Watchman also allow for |
Yet another option would be to change the API of file watching in |
I think I'd like to do the following:
|
I have filed https://youtrack.jetbrains.com/issue/TW-72497 to fix the short names thing with Swabra. The rename+delete thing is not something they should fix I think. |
PR to add the necessary functionality in |
This is now fixed on |
In one build we've seen Gradle fail to compile a subproject (
:build-agent-maven-test-distribution:compileJava
) because it ended up compiling against an outdated version of classes for one of the dependency subprojects (:test-distribution-client
).This is a serious issue as it affects build reliability. Here's the result of a very long investigation about how that happened:
:test-distribution-client:compileJava
with the wrong cache key (3be04def081300037cfdc35044441cae
) – (scan link).So how did we miss these notifications?
It turns out that we've been using a TeamCity pluing called Build Files Cleaner (Swabra) in that specific build. The purpose of this plugin seems to be to restore build checkout directories to their original state before a new build is started. Swabra does two strange things.
First, on Windows, Swabra uses
cmd.exe /c rd /s /q <directory>
to delete some stuff. Doing so actually results in changes being reported via their short paths (stuff likebuild\classes\java\main\com\gradle\ENTERP~1\app\rpc\client\HTTPRP~1.CLA
). This was a feature introduced in Windows 95 to support long file names on FAT, but apparently even files on NTFS have short names, and that's what is used to report changes to them if you userd
. (We might need to follow up on this, but it ended up being a red herring wrt this issue; see gradle/native-platform#289.)Secondly, and this will be important, the previous build of the same project on the same machine failed to check out from Git. Most probably because there was a forced push to the PR or something. The assumption is that this circumstance resulted in Swabra losing track of things, and deleting the checkout directory:
https://builds.gradle.org/buildConfiguration/Enterprise_Master_Component_MavenTestDistributionExtension_CrossVersion_GroupRecentWindows/45017572?buildTab=log&focusLine=42&linesState=38.42
What happened here was that Swabra deleted the whole checkout directory (
C:\tcagent1\work\8d3306ac5f39e07b
), which is also the project root, and thus the directory Gradle was watching. Doing so should have been reported to Gradle, which should have invalidate the relevant parts of the VFS, but that clearly didn't happen. Why?The first impression was that this might be related to the short paths being reported when deleting directories via
cmd.exe /c rd ...
, but it turns out that the root of the deletion is still reported via its long path, so that's not it. It turns out that Swabra uses a different way to delete the whole checkout directory in this case...https://github.com/JetBrains/teamcity-swabra/blob/a6f46f3050c8fb7e71c5d40a871af19eb8152d45/agent/src/jetbrains/buildServer/swabra/Swabra.java#L453-L462
Long story short, Swabra ends up first renaming the checkout directory, and then deleting it.
Now, Windows curiously does not send any notification about the watched directory being renamed, even though it would send a notification if it was deleted. So Gradle stays ignorant of the fact that the project root was moved. Even more curiously, that's still not the cause of our issue, as when the directory is deleted, even in its new location,
native-platform
still reports the events using the original location. So we end up with Gradle learning about the original location being deleted. So then what?Close inspection of the logs above shows that in the build in question, Swabra failed to delete some content in the renamed directory, and hence it did not end up deleting the relocated project directory. In turn, Gradle did not get the notification about the watched directory being deleted (correctly, as it hasn't been), and we kept watching it in its new location (
C:\tcagent1\temp\buildTmp
). However, Gradle keeps thinking that it is watching the project in the old location (C:\tcagent1\work\8d3306ac5f39e07b
).And then TeamCity re-creates the checkout directory (
C:\tcagent1\work\8d3306ac5f39e07b
) and populates it by checking out the code afresh. And while Gradle thinks it watches that location, there are no notifications being sent.Later, when Gradle is invoked to build
C:\tcagent1\work\8d3306ac5f39e07b
again in the same daemon, it thinks whatever is remaining in its VFS is still valid, and does not detect that the source code for:test-distribution-client
has been changed.There.
cc: @gradle/execution, @paplorinc
The text was updated successfully, but these errors were encountered: