Skip to content

Commit b07e554

Browse files
committed
New features and bug fixes:
- 'gvfs prefetch' can now hydrate files as it downloads their blobs - Added a new 'gvfs service' verb for interacting with the GVFS.Service to mount/unmount all repos - Reduced the perf overhead of GvFlt notifications for handle creation - Treat 'git stage' the same as 'git add' - Gather object and packfile counts in 'gvfs diagnose' to help with perf investigations
1 parent ed04790 commit b07e554

File tree

88 files changed

+2385
-783
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+2385
-783
lines changed

GVFS/FastFetch/CheckoutFetchHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public override void FastFetch(string branchOrCommit, bool isBranch)
6868
// Checkout diff output => FindMissingBlobs => BatchDownload => IndexPack => Checkout available blobs
6969
CheckoutJob checkout = new CheckoutJob(this.checkoutThreadCount, this.FolderList, commitToFetch, this.Tracer, this.Enlistment);
7070
FindMissingBlobsJob blobFinder = new FindMissingBlobsJob(this.SearchThreadCount, checkout.RequiredBlobs, checkout.AvailableBlobShas, this.Tracer, this.Enlistment);
71-
BatchObjectDownloadJob downloader = new BatchObjectDownloadJob(this.DownloadThreadCount, this.ChunkSize, blobFinder.DownloadQueue, checkout.AvailableBlobShas, this.Tracer, this.Enlistment, this.ObjectRequestor, this.GitObjects);
71+
BatchObjectDownloadJob downloader = new BatchObjectDownloadJob(this.DownloadThreadCount, this.ChunkSize, blobFinder.MissingBlobs, checkout.AvailableBlobShas, this.Tracer, this.Enlistment, this.ObjectRequestor, this.GitObjects);
7272
IndexPackJob packIndexer = new IndexPackJob(this.IndexThreadCount, downloader.AvailablePacks, checkout.AvailableBlobShas, this.Tracer, this.GitObjects);
7373

7474
// Start pipeline

GVFS/FastFetch/FastFetch.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
<Compile Include="Git\FastFetchLibGit2Repo.cs" />
7575
<Compile Include="Git\GitIndexGenerator.cs" />
7676
<Compile Include="HashingStream.cs" />
77+
<Compile Include="Jobs\ReadFilesJob.cs" />
7778
<Compile Include="WorkingTree.cs" />
7879
<Compile Include="GitEnlistment.cs" />
7980
<Compile Include="Git\DiffHelper.cs" />

GVFS/FastFetch/FetchHelper.cs

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.Collections.Generic;
1111
using System.IO;
1212
using System.Linq;
13+
using System.Threading;
1314

1415
namespace FastFetch
1516
{
@@ -30,7 +31,6 @@ public class FetchHelper
3031
protected readonly bool SkipConfigUpdate;
3132

3233
private const string AreaPath = nameof(FetchHelper);
33-
private const int CommitDepth = 1;
3434

3535
public FetchHelper(
3636
ITracer tracer,
@@ -132,15 +132,24 @@ public static bool TryLoadFileList(Enlistment enlistment, string filesInput, Lis
132132
/// <param name="branchOrCommit">A specific branch to filter for, or null for all branches returned from info/refs</param>
133133
public virtual void FastFetch(string branchOrCommit, bool isBranch)
134134
{
135-
int matchedBlobs;
136-
int downloadedBlobs;
137-
this.FastFetchWithStats(branchOrCommit, isBranch, out matchedBlobs, out downloadedBlobs);
135+
int matchedBlobCount;
136+
int downloadedBlobCount;
137+
int readFileCount;
138+
139+
this.FastFetchWithStats(branchOrCommit, isBranch, false, out matchedBlobCount, out downloadedBlobCount, out readFileCount);
138140
}
139141

140-
public void FastFetchWithStats(string branchOrCommit, bool isBranch, out int matchedBlobs, out int downloadedBlobs)
142+
public void FastFetchWithStats(
143+
string branchOrCommit,
144+
bool isBranch,
145+
bool readFilesAfterDownload,
146+
out int matchedBlobCount,
147+
out int downloadedBlobCount,
148+
out int readFileCount)
141149
{
142-
matchedBlobs = 0;
143-
downloadedBlobs = 0;
150+
matchedBlobCount = 0;
151+
downloadedBlobCount = 0;
152+
readFileCount = 0;
144153

145154
if (string.IsNullOrWhiteSpace(branchOrCommit))
146155
{
@@ -169,16 +178,13 @@ public void FastFetchWithStats(string branchOrCommit, bool isBranch, out int mat
169178
}
170179

171180
this.DownloadMissingCommit(commitToFetch, this.GitObjects);
172-
173-
// Dummy output queue since we don't need to checkout available blobs
174-
BlockingCollection<string> availableBlobs = new BlockingCollection<string>();
175181

176182
// Configure pipeline
177183
// LsTreeHelper output => FindMissingBlobs => BatchDownload => IndexPack
178184
string shallowFile = Path.Combine(this.Enlistment.WorkingDirectoryRoot, GVFSConstants.DotGit.Shallow);
179185

180186
string previousCommit = null;
181-
187+
182188
// Use the shallow file to find a recent commit to diff against to try and reduce the number of SHAs to check
183189
DiffHelper blobEnumerator = new DiffHelper(this.Tracer, this.Enlistment, this.FileList, this.FolderList);
184190
if (File.Exists(shallowFile))
@@ -192,21 +198,32 @@ public void FastFetchWithStats(string branchOrCommit, bool isBranch, out int mat
192198
}
193199
}
194200

195-
blobEnumerator.PerformDiff(previousCommit, commitToFetch);
196-
this.HasFailures |= blobEnumerator.HasFailures;
201+
new Thread(
202+
() =>
203+
{
204+
blobEnumerator.PerformDiff(previousCommit, commitToFetch);
205+
this.HasFailures |= blobEnumerator.HasFailures;
206+
}).Start();
207+
208+
BlockingCollection<string> availableBlobs = new BlockingCollection<string>();
197209

198210
FindMissingBlobsJob blobFinder = new FindMissingBlobsJob(this.SearchThreadCount, blobEnumerator.RequiredBlobs, availableBlobs, this.Tracer, this.Enlistment);
199-
BatchObjectDownloadJob downloader = new BatchObjectDownloadJob(this.DownloadThreadCount, this.ChunkSize, blobFinder.DownloadQueue, availableBlobs, this.Tracer, this.Enlistment, this.ObjectRequestor, this.GitObjects);
211+
BatchObjectDownloadJob downloader = new BatchObjectDownloadJob(this.DownloadThreadCount, this.ChunkSize, blobFinder.MissingBlobs, availableBlobs, this.Tracer, this.Enlistment, this.ObjectRequestor, this.GitObjects);
200212
IndexPackJob packIndexer = new IndexPackJob(this.IndexThreadCount, downloader.AvailablePacks, availableBlobs, this.Tracer, this.GitObjects);
213+
ReadFilesJob readFiles = new ReadFilesJob(Environment.ProcessorCount * 2, blobEnumerator.FileAddOperations, availableBlobs, this.Tracer);
201214

202215
blobFinder.Start();
203216
downloader.Start();
204-
217+
218+
if (readFilesAfterDownload)
219+
{
220+
readFiles.Start();
221+
}
222+
205223
// If indexing happens during searching, searching progressively gets slower, so wait on searching before indexing.
206224
blobFinder.WaitForCompletion();
207225
this.HasFailures |= blobFinder.HasFailures;
208226

209-
// Index regardless of failures, it'll shorten the next fetch.
210227
packIndexer.Start();
211228

212229
downloader.WaitForCompletion();
@@ -215,8 +232,17 @@ public void FastFetchWithStats(string branchOrCommit, bool isBranch, out int mat
215232
packIndexer.WaitForCompletion();
216233
this.HasFailures |= packIndexer.HasFailures;
217234

218-
matchedBlobs = blobFinder.AvailableBlobCount + blobFinder.MissingBlobCount;
219-
downloadedBlobs = blobFinder.MissingBlobCount;
235+
availableBlobs.CompleteAdding();
236+
237+
if (readFilesAfterDownload)
238+
{
239+
readFiles.WaitForCompletion();
240+
this.HasFailures |= readFiles.HasFailures;
241+
}
242+
243+
matchedBlobCount = blobFinder.AvailableBlobCount + blobFinder.MissingBlobCount;
244+
downloadedBlobCount = blobFinder.MissingBlobCount;
245+
readFileCount = readFiles.ReadFileCount;
220246

221247
if (!this.SkipConfigUpdate && !this.HasFailures)
222248
{
@@ -313,15 +339,14 @@ protected void DownloadMissingCommit(string commitSha, GitObjects gitObjects)
313339
{
314340
EventMetadata startMetadata = new EventMetadata();
315341
startMetadata.Add("CommitSha", commitSha);
316-
startMetadata.Add("CommitDepth", CommitDepth);
317342

318343
using (ITracer activity = this.Tracer.StartActivity("DownloadTrees", EventLevel.Informational, Keywords.Telemetry, startMetadata))
319344
{
320345
using (FastFetchLibGit2Repo repo = new FastFetchLibGit2Repo(this.Tracer, this.Enlistment.WorkingDirectoryRoot))
321346
{
322347
if (!repo.ObjectExists(commitSha))
323348
{
324-
if (!gitObjects.TryEnsureCommitIsLocal(commitSha, commitDepth: CommitDepth))
349+
if (!gitObjects.TryDownloadCommit(commitSha))
325350
{
326351
EventMetadata metadata = new EventMetadata();
327352
metadata.Add("ObjectsEndpointUrl", this.ObjectRequestor.CacheServer.ObjectsEndpointUrl);

GVFS/FastFetch/Git/GitIndexGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ private void WriteAllEntries(uint version, HashSet<string> sparseCheckoutEntries
108108

109109
uint lastStringLength = 0;
110110
LsTreeEntry entry;
111-
while (this.entryQueue.TryTake(out entry, millisecondsTimeout: -1))
111+
while (this.entryQueue.TryTake(out entry, Timeout.Infinite))
112112
{
113113
bool skipWorkTree =
114114
sparseCheckoutEntries != null &&

GVFS/FastFetch/Jobs/BatchObjectDownloadJob.cs

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public class BatchObjectDownloadJob : Job
2424

2525
private static readonly TimeSpan HeartBeatPeriod = TimeSpan.FromSeconds(20);
2626

27-
private readonly BlockingAggregator<string, BlobDownloadRequest> inputQueue;
27+
private readonly DownloadRequestAggregator downloadRequests;
2828

2929
private int activeDownloadCount;
3030

@@ -39,7 +39,7 @@ public class BatchObjectDownloadJob : Job
3939
public BatchObjectDownloadJob(
4040
int maxParallel,
4141
int chunkSize,
42-
BlockingCollection<string> inputQueue,
42+
BlockingCollection<string> missingBlobs,
4343
BlockingCollection<string> availableBlobs,
4444
ITracer tracer,
4545
Enlistment enlistment,
@@ -49,7 +49,7 @@ public BatchObjectDownloadJob(
4949
{
5050
this.tracer = tracer.StartActivity(AreaPath, EventLevel.Informational, Keywords.Telemetry, metadata: null);
5151

52-
this.inputQueue = new BlockingAggregator<string, BlobDownloadRequest>(inputQueue, chunkSize, objectIds => new BlobDownloadRequest(objectIds));
52+
this.downloadRequests = new DownloadRequestAggregator(missingBlobs, chunkSize);
5353

5454
this.enlistment = enlistment;
5555
this.objectRequestor = objectRequestor;
@@ -73,7 +73,7 @@ protected override void DoBeforeWork()
7373
protected override void DoWork()
7474
{
7575
BlobDownloadRequest request;
76-
while (this.inputQueue.TryTake(out request))
76+
while (this.downloadRequests.TryTake(out request))
7777
{
7878
Interlocked.Increment(ref this.activeDownloadCount);
7979

@@ -89,7 +89,6 @@ protected override void DoWork()
8989
HashSet<string> successfulDownloads = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
9090
RetryWrapper<GitObjectsHttpRequestor.GitObjectTaskResult>.InvocationResult result = this.objectRequestor.TryDownloadObjects(
9191
() => request.ObjectIds.Except(successfulDownloads),
92-
commitDepth: 1,
9392
onSuccess: (tryCount, response) => this.WriteObjectOrPack(request, tryCount, response, successfulDownloads),
9493
onFailure: RetryWrapper<GitObjectsHttpRequestor.GitObjectTaskResult>.StandardErrorHandler(activity, request.PackId, DownloadAreaPath),
9594
preferBatchedLooseObjects: true);
@@ -199,42 +198,45 @@ private void EmitHeartbeat(object state)
199198
this.tracer.RelatedEvent(EventLevel.Verbose, "DownloadHeartbeat", metadata);
200199
}
201200

202-
private class BlockingAggregator<InputType, OutputType>
201+
private class DownloadRequestAggregator
203202
{
204-
private BlockingCollection<InputType> inputQueue;
203+
private BlockingCollection<string> missingBlobs;
205204
private int chunkSize;
206-
private Func<List<InputType>, OutputType> factory;
207205

208-
public BlockingAggregator(BlockingCollection<InputType> input, int chunkSize, Func<List<InputType>, OutputType> factory)
206+
public DownloadRequestAggregator(BlockingCollection<string> missingBlobs, int chunkSize)
209207
{
210-
this.inputQueue = input;
208+
this.missingBlobs = missingBlobs;
211209
this.chunkSize = chunkSize;
212-
this.factory = factory;
213210
}
214211

215-
public bool TryTake(out OutputType output)
212+
public bool TryTake(out BlobDownloadRequest request)
216213
{
217-
List<InputType> intermediary = new List<InputType>();
214+
List<string> blobsInChunk = new List<string>();
215+
218216
for (int i = 0; i < this.chunkSize; ++i)
219217
{
220-
InputType data;
221-
if (this.inputQueue.TryTake(out data, millisecondsTimeout: -1))
218+
// Only wait a short while for new work to show up, otherwise go ahead and download what we have accumulated so far
219+
const int TimeoutMs = 100;
220+
221+
string blobId;
222+
if (this.missingBlobs.TryTake(out blobId, TimeoutMs))
222223
{
223-
intermediary.Add(data);
224+
blobsInChunk.Add(blobId);
224225
}
225-
else
226+
else if (blobsInChunk.Count > 0 ||
227+
this.missingBlobs.IsAddingCompleted)
226228
{
227229
break;
228230
}
229231
}
230232

231-
if (intermediary.Any())
233+
if (blobsInChunk.Count > 0)
232234
{
233-
output = this.factory(intermediary);
235+
request = new BlobDownloadRequest(blobsInChunk);
234236
return true;
235237
}
236238

237-
output = default(OutputType);
239+
request = null;
238240
return false;
239241
}
240242
}

GVFS/FastFetch/Jobs/CheckoutJob.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ private void HandleAllFileAddOperations()
282282
using (FastFetchLibGit2Repo repo = new FastFetchLibGit2Repo(this.tracer, this.enlistment.WorkingDirectoryRoot))
283283
{
284284
string availableBlob;
285-
while (this.AvailableBlobShas.TryTake(out availableBlob, millisecondsTimeout: -1))
285+
while (this.AvailableBlobShas.TryTake(out availableBlob, Timeout.Infinite))
286286
{
287287
if (this.HasFailures)
288288
{

GVFS/FastFetch/Jobs/Data/BlobDownloadRequest.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System.Collections.Generic;
2-
using System.Linq;
32
using System.Threading;
43

54
namespace FastFetch.Jobs.Data

GVFS/FastFetch/Jobs/Data/IndexPackRequest.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Text;
5-
using System.Threading.Tasks;
6-
7-
namespace FastFetch.Jobs.Data
1+
namespace FastFetch.Jobs.Data
82
{
93
public class IndexPackRequest
104
{

GVFS/FastFetch/Jobs/FindMissingBlobsJob.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,28 @@ public class FindMissingBlobsJob : Job
2020
private int missingBlobCount;
2121
private int availableBlobCount;
2222

23-
private BlockingCollection<string> inputQueue;
23+
private BlockingCollection<string> requiredBlobs;
2424

2525
private ConcurrentHashSet<string> alreadyFoundBlobIds;
2626

2727
public FindMissingBlobsJob(
2828
int maxParallel,
29-
BlockingCollection<string> inputQueue,
29+
BlockingCollection<string> requiredBlobs,
3030
BlockingCollection<string> availableBlobs,
3131
ITracer tracer,
3232
Enlistment enlistment)
3333
: base(maxParallel)
3434
{
3535
this.tracer = tracer.StartActivity(AreaPath, EventLevel.Informational, Keywords.Telemetry, metadata: null);
36-
this.inputQueue = inputQueue;
36+
this.requiredBlobs = requiredBlobs;
3737
this.enlistment = enlistment;
3838
this.alreadyFoundBlobIds = new ConcurrentHashSet<string>();
3939

40-
this.DownloadQueue = new BlockingCollection<string>();
40+
this.MissingBlobs = new BlockingCollection<string>();
4141
this.AvailableBlobs = availableBlobs;
4242
}
4343

44-
public BlockingCollection<string> DownloadQueue { get; }
44+
public BlockingCollection<string> MissingBlobs { get; }
4545
public BlockingCollection<string> AvailableBlobs { get; }
4646

4747
public int MissingBlobCount
@@ -59,14 +59,14 @@ protected override void DoWork()
5959
string blobId;
6060
using (FastFetchLibGit2Repo repo = new FastFetchLibGit2Repo(this.tracer, this.enlistment.WorkingDirectoryRoot))
6161
{
62-
while (this.inputQueue.TryTake(out blobId, Timeout.Infinite))
62+
while (this.requiredBlobs.TryTake(out blobId, Timeout.Infinite))
6363
{
6464
if (this.alreadyFoundBlobIds.Add(blobId))
6565
{
6666
if (!repo.ObjectExists(blobId))
6767
{
6868
Interlocked.Increment(ref this.missingBlobCount);
69-
this.DownloadQueue.Add(blobId);
69+
this.MissingBlobs.Add(blobId);
7070
}
7171
else
7272
{
@@ -80,7 +80,7 @@ protected override void DoWork()
8080

8181
protected override void DoAfterWork()
8282
{
83-
this.DownloadQueue.CompleteAdding();
83+
this.MissingBlobs.CompleteAdding();
8484

8585
EventMetadata metadata = new EventMetadata();
8686
metadata.Add("TotalMissingObjects", this.missingBlobCount);

GVFS/FastFetch/Jobs/IndexPackJob.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class IndexPackJob : Job
1212
private const string AreaPath = "IndexPackJob";
1313
private const string IndexPackAreaPath = "IndexPack";
1414

15-
private readonly BlockingCollection<IndexPackRequest> inputQueue;
15+
private readonly BlockingCollection<IndexPackRequest> availablePacks;
1616

1717
private ITracer tracer;
1818
private GitObjects gitObjects;
@@ -21,14 +21,14 @@ public class IndexPackJob : Job
2121

2222
public IndexPackJob(
2323
int maxParallel,
24-
BlockingCollection<IndexPackRequest> inputQueue,
24+
BlockingCollection<IndexPackRequest> availablePacks,
2525
BlockingCollection<string> availableBlobs,
2626
ITracer tracer,
2727
GitObjects gitObjects)
2828
: base(maxParallel)
2929
{
3030
this.tracer = tracer.StartActivity(AreaPath, EventLevel.Informational, Keywords.Telemetry, metadata: null);
31-
this.inputQueue = inputQueue;
31+
this.availablePacks = availablePacks;
3232
this.gitObjects = gitObjects;
3333
this.AvailableBlobs = availableBlobs;
3434
}
@@ -38,7 +38,7 @@ public IndexPackJob(
3838
protected override void DoWork()
3939
{
4040
IndexPackRequest request;
41-
while (this.inputQueue.TryTake(out request, millisecondsTimeout: -1))
41+
while (this.availablePacks.TryTake(out request, Timeout.Infinite))
4242
{
4343
EventMetadata metadata = new EventMetadata();
4444
metadata.Add("PackId", request.DownloadRequest.PackId);

0 commit comments

Comments
 (0)