Skip to content

Commit df6d655

Browse files
committed
Merge pull request git-for-windows#227: gvfs-helper: add prefetch action
This replaces git-for-windows#223. There was a strangely-subtle issue about reading the trailing hash from the downloaded packs that caused issues when reading from the origin remote. Add `gvfs-helper prefetch` command line option and `objects.prefetch` mode in `gvfs-helper server`. Sorry, but this contains a major refactor of the packfile and loose file handling to let me share it with the prefetch code. As a side benefit, I collapsed the tempfile creation before the request goes out and merged the install_ code after the result is returned. I also changed packfile code to use the packfile-checksum rather than a timestamp so that we look more like normal Git. More details are in the commit message.
2 parents c51cf80 + 1103486 commit df6d655

5 files changed

+1600
-340
lines changed

gvfs-helper-client.c

+116-13
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@ static struct hashmap gh_server__subprocess_map;
2424
static struct object_directory *gh_client__chosen_odb;
2525

2626
/*
27-
* The "objects" capability has 2 verbs: "get" and "post".
27+
* The "objects" capability has verbs: "get" and "post" and "prefetch".
2828
*/
2929
#define CAP_OBJECTS (1u<<1)
3030
#define CAP_OBJECTS_NAME "objects"
3131

3232
#define CAP_OBJECTS__VERB_GET1_NAME "get"
3333
#define CAP_OBJECTS__VERB_POST_NAME "post"
34+
#define CAP_OBJECTS__VERB_PREFETCH_NAME "prefetch"
3435

3536
static int gh_client__start_fn(struct subprocess_entry *subprocess)
3637
{
@@ -129,6 +130,44 @@ static int gh_client__send__objects_get(struct child_process *process,
129130
return 0;
130131
}
131132

133+
/*
134+
* Send a request to gvfs-helper to prefetch packfiles from either the
135+
* cache-server or the main Git server using "/gvfs/prefetch".
136+
*
137+
* objects.prefetch LF
138+
* [<seconds-since_epoch> LF]
139+
* <flush>
140+
*/
141+
static int gh_client__send__objects_prefetch(struct child_process *process,
142+
timestamp_t seconds_since_epoch)
143+
{
144+
int err;
145+
146+
/*
147+
* We assume that all of the packet_ routines call error()
148+
* so that we don't have to.
149+
*/
150+
151+
err = packet_write_fmt_gently(
152+
process->in,
153+
(CAP_OBJECTS_NAME "." CAP_OBJECTS__VERB_PREFETCH_NAME "\n"));
154+
if (err)
155+
return err;
156+
157+
if (seconds_since_epoch) {
158+
err = packet_write_fmt_gently(process->in, "%" PRItime "\n",
159+
seconds_since_epoch);
160+
if (err)
161+
return err;
162+
}
163+
164+
err = packet_flush_gently(process->in);
165+
if (err)
166+
return err;
167+
168+
return 0;
169+
}
170+
132171
/*
133172
* Verify that the pathname found in the "odb" response line matches
134173
* what we requested.
@@ -198,7 +237,7 @@ static void gh_client__update_packed_git(const char *line)
198237
}
199238

200239
/*
201-
* Both CAP_OBJECTS verbs return the same format response:
240+
* CAP_OBJECTS verbs return the same format response:
202241
*
203242
* <odb>
204243
* <data>*
@@ -238,6 +277,8 @@ static int gh_client__objects__receive_response(
238277
const char *v1;
239278
char *line;
240279
int len;
280+
int nr_loose = 0;
281+
int nr_packfile = 0;
241282
int err = 0;
242283

243284
while (1) {
@@ -256,13 +297,13 @@ static int gh_client__objects__receive_response(
256297
else if (starts_with(line, "packfile")) {
257298
gh_client__update_packed_git(line);
258299
ghc |= GHC__CREATED__PACKFILE;
259-
*p_nr_packfile += 1;
300+
nr_packfile++;
260301
}
261302

262303
else if (starts_with(line, "loose")) {
263304
gh_client__update_loose_cache(line);
264305
ghc |= GHC__CREATED__LOOSE;
265-
*p_nr_loose += 1;
306+
nr_loose++;
266307
}
267308

268309
else if (starts_with(line, "ok"))
@@ -276,6 +317,8 @@ static int gh_client__objects__receive_response(
276317
}
277318

278319
*p_ghc = ghc;
320+
*p_nr_loose = nr_loose;
321+
*p_nr_packfile = nr_packfile;
279322

280323
return err;
281324
}
@@ -333,7 +376,7 @@ static struct gh_server__process *gh_client__find_long_running_process(
333376
/*
334377
* Find an existing long-running process with the above command
335378
* line -or- create a new long-running process for this and
336-
* subsequent 'get' requests.
379+
* subsequent requests.
337380
*/
338381
if (!gh_server__subprocess_map_initialized) {
339382
gh_server__subprocess_map_initialized = 1;
@@ -370,10 +413,14 @@ static struct gh_server__process *gh_client__find_long_running_process(
370413

371414
void gh_client__queue_oid(const struct object_id *oid)
372415
{
373-
// TODO consider removing this trace2. it is useful for interactive
374-
// TODO debugging, but may generate way too much noise for a data
375-
// TODO event.
376-
trace2_printf("gh_client__queue_oid: %s", oid_to_hex(oid));
416+
/*
417+
* Keep this trace as a printf only, so that it goes to the
418+
* perf log, but not the event log. It is useful for interactive
419+
* debugging, but generates way too much (unuseful) noise for the
420+
* database.
421+
*/
422+
if (trace2_is_enabled())
423+
trace2_printf("gh_client__queue_oid: %s", oid_to_hex(oid));
377424

378425
if (!oidset_insert(&gh_client__oidset_queued, oid))
379426
gh_client__oidset_count++;
@@ -454,10 +501,14 @@ int gh_client__get_immediate(const struct object_id *oid,
454501
int nr_packfile = 0;
455502
int err = 0;
456503

457-
// TODO consider removing this trace2. it is useful for interactive
458-
// TODO debugging, but may generate way too much noise for a data
459-
// TODO event.
460-
trace2_printf("gh_client__get_immediate: %s", oid_to_hex(oid));
504+
/*
505+
* Keep this trace as a printf only, so that it goes to the
506+
* perf log, but not the event log. It is useful for interactive
507+
* debugging, but generates way too much (unuseful) noise for the
508+
* database.
509+
*/
510+
if (trace2_is_enabled())
511+
trace2_printf("gh_client__get_immediate: %s", oid_to_hex(oid));
461512

462513
entry = gh_client__find_long_running_process(CAP_OBJECTS);
463514
if (!entry)
@@ -486,3 +537,55 @@ int gh_client__get_immediate(const struct object_id *oid,
486537

487538
return err;
488539
}
540+
541+
/*
542+
* Ask gvfs-helper to prefetch commits-and-trees packfiles since a
543+
* given timestamp.
544+
*
545+
* If seconds_since_epoch is zero, gvfs-helper will scan the ODB for
546+
* the last received prefetch and ask for ones newer than that.
547+
*/
548+
int gh_client__prefetch(timestamp_t seconds_since_epoch,
549+
int *nr_packfiles_received)
550+
{
551+
struct gh_server__process *entry;
552+
struct child_process *process;
553+
enum gh_client__created ghc;
554+
int nr_loose = 0;
555+
int nr_packfile = 0;
556+
int err = 0;
557+
558+
entry = gh_client__find_long_running_process(CAP_OBJECTS);
559+
if (!entry)
560+
return -1;
561+
562+
trace2_region_enter("gh-client", "objects/prefetch", the_repository);
563+
trace2_data_intmax("gh-client", the_repository, "prefetch/since",
564+
seconds_since_epoch);
565+
566+
process = &entry->subprocess.process;
567+
568+
sigchain_push(SIGPIPE, SIG_IGN);
569+
570+
err = gh_client__send__objects_prefetch(process, seconds_since_epoch);
571+
if (!err)
572+
err = gh_client__objects__receive_response(
573+
process, &ghc, &nr_loose, &nr_packfile);
574+
575+
sigchain_pop(SIGPIPE);
576+
577+
if (err) {
578+
subprocess_stop(&gh_server__subprocess_map,
579+
(struct subprocess_entry *)entry);
580+
FREE_AND_NULL(entry);
581+
}
582+
583+
trace2_data_intmax("gh-client", the_repository,
584+
"prefetch/packfile_count", nr_packfile);
585+
trace2_region_leave("gh-client", "objects/prefetch", the_repository);
586+
587+
if (nr_packfiles_received)
588+
*nr_packfiles_received = nr_packfile;
589+
590+
return err;
591+
}

gvfs-helper-client.h

+18
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,22 @@ void gh_client__queue_oid_array(const struct object_id *oids, int oid_nr);
6666
*/
6767
int gh_client__drain_queue(enum gh_client__created *p_ghc);
6868

69+
/*
70+
* Ask `gvfs-helper server` to fetch any "prefetch packs"
71+
* available on the server more recent than the requested time.
72+
*
73+
* If seconds_since_epoch is zero, gvfs-helper will scan the ODB for
74+
* the last received prefetch and ask for ones newer than that.
75+
*
76+
* A long-running background process is used to subsequent requests
77+
* (either prefetch or regular immediate/queued requests) more efficient.
78+
*
79+
* One or more packfiles will be created in the shared-cache ODB.
80+
*
81+
* Returns 0 on success, -1 on error. Optionally also returns the
82+
* number of prefetch packs received.
83+
*/
84+
int gh_client__prefetch(timestamp_t seconds_since_epoch,
85+
int *nr_packfiles_received);
86+
6987
#endif /* GVFS_HELPER_CLIENT_H */

0 commit comments

Comments
 (0)