Skip to content

Commit 1065f33

Browse files
jeffhostetlerdscho
authored andcommitted
gvfs-helper: add prefetch support
Teach gvfs-helper to support "/gvfs/prefetch" REST API. This includes a new `gvfs-helper prefetch --since=<t>` command line option. And a new `objects.prefetch` verb in `gvfs-helper server` mode. If `since` argument is omitted, `gvfs-helper` will search the local shared-cache for the most recent prefetch packfile and start from there. The <t> is usually a seconds-since-epoch, but may also be a "friendly" date -- such as "midnight", "yesterday" and etc. using the existing date selection mechanism. Add `gh_client__prefetch()` API to allow `git.exe` to easily call prefetch (and using the same long-running process as immediate and queued object fetches). Expanded t5799 unit tests to include prefetch tests. Test setup now also builds some commits-and-trees packfiles for testing purposes with well-known timestamps. Expanded t/helper/test-gvfs-protocol.exe to support "/gvfs/prefetch" REST API. Massive refactor of existing packfile handling in gvfs-helper.c to reuse more code between "/gvfs/objects POST" and "/gvfs/prefetch". With this we now properly name packfiles with the checksum SHA1 rather than a date string. Refactor also addresses some of the confusing tempfile setup and install_<result> code processing (introduced to handle the ambiguity of how POST works with commit objects). Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
1 parent fb21132 commit 1065f33

File tree

5 files changed

+1502
-339
lines changed

5 files changed

+1502
-339
lines changed

gvfs-helper-client.c

Lines changed: 116 additions & 13 deletions
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
* Update the loose object cache to include the newly created
134173
* object.
@@ -176,7 +215,7 @@ static void gh_client__update_packed_git(const char *line)
176215
}
177216

178217
/*
179-
* Both CAP_OBJECTS verbs return the same format response:
218+
* CAP_OBJECTS verbs return the same format response:
180219
*
181220
* <odb>
182221
* <data>*
@@ -216,6 +255,8 @@ static int gh_client__objects__receive_response(
216255
const char *v1;
217256
char *line;
218257
int len;
258+
int nr_loose = 0;
259+
int nr_packfile = 0;
219260
int err = 0;
220261

221262
while (1) {
@@ -234,13 +275,13 @@ static int gh_client__objects__receive_response(
234275
else if (starts_with(line, "packfile")) {
235276
gh_client__update_packed_git(line);
236277
ghc |= GHC__CREATED__PACKFILE;
237-
*p_nr_packfile += 1;
278+
nr_packfile++;
238279
}
239280

240281
else if (starts_with(line, "loose")) {
241282
gh_client__update_loose_cache(line);
242283
ghc |= GHC__CREATED__LOOSE;
243-
*p_nr_loose += 1;
284+
nr_loose++;
244285
}
245286

246287
else if (starts_with(line, "ok"))
@@ -254,6 +295,8 @@ static int gh_client__objects__receive_response(
254295
}
255296

256297
*p_ghc = ghc;
298+
*p_nr_loose = nr_loose;
299+
*p_nr_packfile = nr_packfile;
257300

258301
return err;
259302
}
@@ -310,7 +353,7 @@ static struct gh_server__process *gh_client__find_long_running_process(
310353
/*
311354
* Find an existing long-running process with the above command
312355
* line -or- create a new long-running process for this and
313-
* subsequent 'get' requests.
356+
* subsequent requests.
314357
*/
315358
if (!gh_server__subprocess_map_initialized) {
316359
gh_server__subprocess_map_initialized = 1;
@@ -347,10 +390,14 @@ static struct gh_server__process *gh_client__find_long_running_process(
347390

348391
void gh_client__queue_oid(const struct object_id *oid)
349392
{
350-
// TODO consider removing this trace2. it is useful for interactive
351-
// TODO debugging, but may generate way too much noise for a data
352-
// TODO event.
353-
trace2_printf("gh_client__queue_oid: %s", oid_to_hex(oid));
393+
/*
394+
* Keep this trace as a printf only, so that it goes to the
395+
* perf log, but not the event log. It is useful for interactive
396+
* debugging, but generates way too much (unuseful) noise for the
397+
* database.
398+
*/
399+
if (trace2_is_enabled())
400+
trace2_printf("gh_client__queue_oid: %s", oid_to_hex(oid));
354401

355402
if (!oidset_insert(&gh_client__oidset_queued, oid))
356403
gh_client__oidset_count++;
@@ -431,10 +478,14 @@ int gh_client__get_immediate(const struct object_id *oid,
431478
int nr_packfile = 0;
432479
int err = 0;
433480

434-
// TODO consider removing this trace2. it is useful for interactive
435-
// TODO debugging, but may generate way too much noise for a data
436-
// TODO event.
437-
trace2_printf("gh_client__get_immediate: %s", oid_to_hex(oid));
481+
/*
482+
* Keep this trace as a printf only, so that it goes to the
483+
* perf log, but not the event log. It is useful for interactive
484+
* debugging, but generates way too much (unuseful) noise for the
485+
* database.
486+
*/
487+
if (trace2_is_enabled())
488+
trace2_printf("gh_client__get_immediate: %s", oid_to_hex(oid));
438489

439490
entry = gh_client__find_long_running_process(CAP_OBJECTS);
440491
if (!entry)
@@ -463,3 +514,55 @@ int gh_client__get_immediate(const struct object_id *oid,
463514

464515
return err;
465516
}
517+
518+
/*
519+
* Ask gvfs-helper to prefetch commits-and-trees packfiles since a
520+
* given timestamp.
521+
*
522+
* If seconds_since_epoch is zero, gvfs-helper will scan the ODB for
523+
* the last received prefetch and ask for ones newer than that.
524+
*/
525+
int gh_client__prefetch(timestamp_t seconds_since_epoch,
526+
int *nr_packfiles_received)
527+
{
528+
struct gh_server__process *entry;
529+
struct child_process *process;
530+
enum gh_client__created ghc;
531+
int nr_loose = 0;
532+
int nr_packfile = 0;
533+
int err = 0;
534+
535+
entry = gh_client__find_long_running_process(CAP_OBJECTS);
536+
if (!entry)
537+
return -1;
538+
539+
trace2_region_enter("gh-client", "objects/prefetch", the_repository);
540+
trace2_data_intmax("gh-client", the_repository, "prefetch/since",
541+
seconds_since_epoch);
542+
543+
process = &entry->subprocess.process;
544+
545+
sigchain_push(SIGPIPE, SIG_IGN);
546+
547+
err = gh_client__send__objects_prefetch(process, seconds_since_epoch);
548+
if (!err)
549+
err = gh_client__objects__receive_response(
550+
process, &ghc, &nr_loose, &nr_packfile);
551+
552+
sigchain_pop(SIGPIPE);
553+
554+
if (err) {
555+
subprocess_stop(&gh_server__subprocess_map,
556+
(struct subprocess_entry *)entry);
557+
FREE_AND_NULL(entry);
558+
}
559+
560+
trace2_data_intmax("gh-client", the_repository,
561+
"prefetch/packfile_count", nr_packfile);
562+
trace2_region_leave("gh-client", "objects/prefetch", the_repository);
563+
564+
if (nr_packfiles_received)
565+
*nr_packfiles_received = nr_packfile;
566+
567+
return err;
568+
}

gvfs-helper-client.h

Lines changed: 18 additions & 0 deletions
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)