-
Notifications
You must be signed in to change notification settings - Fork 649
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
A lock file implementation applied to project GitVersionCore for resolving #1031 #2333
Conversation
I ask you, which timeout is necessary. I just took 15 seconds. I am worried about the fact, that the assemblies are often cached, but the servicecollection filled and serviceprovider built is always happening once in a task, so it will always dispose correct at end of any task in GitVersionTask.MSBuild assembly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for starting to look into this, @teroneko! 🙏
I ask you, which timeout is necessary. I just took 15 seconds.
We have reports that GitVersion is taking more than a minute to complete, so I'm not sure 15 seconds is enough. #1448 (comment)
I am worried about the fact, that the assemblies are often cached, but the servicecollection filled and serviceprovider built is always happening once in a task, so it will always dispose correct at end of any task in GitVersionTask.MSBuild assembly.
If I understand you correctly, that's the source of the problem and why this is so hard to fix: The lock needs to exist and work across processes. Locking inside the GitVersion process won't help, since we have many instances of GitVersion running in parallel in different processes.
At the evening I can spend some time I hope. Time is rare. About your time consideration. So it could happen then, that one process is staling then for about 1 minute for wating to acquire the lock until it continues its IServiceCollection build process, otherwise a TimeoutException will be thrown. |
See comment above.
The point is we have to ensure, that each Task (from MSBuild) in GitVersionTask.MSbuild assembly is correctly disposing the singleton IFileLock. (in this current state of implementation - changed later). We should not rely on it, that Garbage Colelctor is doing it as some time, when built ServiceProvider is out of scope beside the fact, that the assembly can be cached and its automatical dispose is not guaranteed. So as you can see is IServiceProvider not implementing IDisposable, but ServiceProvider in assembly Microsoft.Extensions.DependencyInjection is doing so. So we have to return a derived IServiceProvider implementation that implements IDisposable and write a wrapper like I did for IFileLock. With such exposed custom IServiceProvider we can now manually dispose Microsoft.Extensions.DependencyInjection.ServiceProvider your just crafted at
|
Yes, I agree. We should explicitly dispose as soon as we are done indirectly poking the file system with LibGit2Sharp. |
…an produce FileLockUses; FileLock wraps FileLockUse and implements IDisposable for being cleaned when disposing ServiceProvider
This pull request introduces 1 alert when merging e6754e2 into 5330bbb - view on LGTM.com new alerts:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I implement it?
I don't feel responsible.
@@ -78,6 +78,11 @@ public IEnumerable<string> DirectoryEnumerateFiles(string directory, string sear | |||
throw new NotImplementedException(); | |||
} | |||
|
|||
public FileStream Open(string path, FileMode mode, FileAccess access, FileShare share) | |||
{ | |||
return new FileStream(path, mode, access, share); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TestStream is deriving from Stream, but need Lock implementation of FileStream for LockFileApi. So when needed, another Stream wrapper have to be written for FileStream.
…executed action in assembly GitVersion.MSBuildTask
src/GitVersionCore/Core/Abstractions/IDisposableServiceProvider.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fantastic work, @teroneko! 👏 🎉
But as with any feature as big as this, there are things we need to discuss before this is ready to be merged. We also need to figure out how to test this.
src/GitVersionCore/Core/Abstractions/IDisposableServiceProvider.cs
Outdated
Show resolved
Hide resolved
throw new InvalidOperationException("You cannot decrease lock use when no file stream has been assgined."); | ||
} | ||
|
||
var decreaseLockUseLocker = this.decreaseLockUseLocker; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assignment seems unnecessary and makes me think twice about what the below lock
statement does. Does it lock the class field or the local variable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does lock the Unlock function. You will see that there is another lock in FileLocker in Unlock. These calls can interfer each other and can lead to race conditions like the comment does tell you.
|
||
#nullable enable | ||
|
||
public struct FileLockUse : IDisposable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't quite understand what FileLockUse
does by its name. Can you please explain so we can perhaps figure out a more self-descriptive and intuitive name for it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It describes the use of a file lock. As one lock is shared by many I thought the term "use" of one file lock would be somewhat self-explanatory.
|
||
namespace GitVersion.FileLocking | ||
{ | ||
public interface ILockFileApi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't quite understand the distinction between IFileLocker
andILockFileApi
. Why are they separated? I'm also not a fan of using Api
in the name of an interface. All C# interfaces (and public classes) are application programming interfaces. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided to implement this in a composition flavor. When the "API" is about to have the ability to lock anything the FileLocker can lock a specific file that has the ability to lock one file but being callable by many concurrent calls to FileLocker.Lock(). So the file gets unlocked first when all current FileLockUse instances are got disposed. So later you can graduate the use of the instance of FileLocker to your behave when you decide to go away from one global lock.
Remove because of false impression Co-authored-by: Asbjørn Ulsberg <[email protected]>
Remove because of false impresssion Co-authored-by: Asbjørn Ulsberg <[email protected]>
Remove due to implicit copyright Co-authored-by: Asbjørn Ulsberg <[email protected]>
Co-authored-by: Asbjørn Ulsberg <[email protected]>
Remove because of false impression Co-authored-by: Asbjørn Ulsberg <[email protected]>
When I get some free time I would set up your prepared repository. Currently I am just too busy. |
Is there way to easy get a Version of the BuildTask with this fix in? (like a alpha nuget package) so that we can test this in the wild? |
@DerAlbertCom Unfortunately I have no time because of business work. I can repeat the words of @asbjornu that you are free to assist in this pull request. But I'm looking forward to spend time as soon as I can. I'd like to see this feature too. If you are really in need of a solution right now, you can use my experimental workaround. Use it only when you need pacakge level versioning (package name & .nuspec-file, that file that is is generated exclusively for package), but not compile level versioning (Assembly file). It does mimic GitVersionTask behaviour and supports concurrency with the above implementation. It does so by wrapping GitVersion.exe and locking file at path "<repository-root>/.gvc/index" when talking to GitVersion.exe. Use this package as you do with GitVersionTask. |
@teroneko, I'm sorry to see this closed. Would a viable road ahead be to add this feature behind a configuration option so we can ship it for testing? Then if people report back that this actually fixes the problem, we can consider removing the configuartion in v6 and just make this the default. |
This issue has been automatically marked as stale because it has not had recent activity. After 30 days from now, it will be closed if no further activity occurs. Thank you for your contributions. |
No stale please. Any news? |
Closing this one in favor of #2410 |
A lock file implementation applied to project GitVersionCore for resolving #1031.
Description
Inject singleton service IFileLock to ServiceCollection. Lock gets disposed when the ServiceProvider is getting disposed too.
Related Issue
See #1031.
Motivation and Context
There were many failing builds in CI environments due to parallelism.
How Has This Been Tested?
Not yet tested...
Screenshots (if appropriate):
Checklist: