@@ -28,13 +28,14 @@ static struct hashmap gh_server__subprocess_map;
2828static struct object_directory * gh_client__chosen_odb ;
2929
3030/*
31- * The "objects" capability has 2 verbs: "get" and "post".
31+ * The "objects" capability has verbs: "get" and "post" and "prefetch ".
3232 */
3333#define CAP_OBJECTS (1u<<1)
3434#define CAP_OBJECTS_NAME "objects"
3535
3636#define CAP_OBJECTS__VERB_GET1_NAME "get"
3737#define CAP_OBJECTS__VERB_POST_NAME "post"
38+ #define CAP_OBJECTS__VERB_PREFETCH_NAME "prefetch"
3839
3940static int gh_client__start_fn (struct subprocess_entry * subprocess )
4041{
@@ -133,6 +134,44 @@ static int gh_client__send__objects_get(struct child_process *process,
133134 return 0 ;
134135}
135136
137+ /*
138+ * Send a request to gvfs-helper to prefetch packfiles from either the
139+ * cache-server or the main Git server using "/gvfs/prefetch".
140+ *
141+ * objects.prefetch LF
142+ * [<seconds-since_epoch> LF]
143+ * <flush>
144+ */
145+ static int gh_client__send__objects_prefetch (struct child_process * process ,
146+ timestamp_t seconds_since_epoch )
147+ {
148+ int err ;
149+
150+ /*
151+ * We assume that all of the packet_ routines call error()
152+ * so that we don't have to.
153+ */
154+
155+ err = packet_write_fmt_gently (
156+ process -> in ,
157+ (CAP_OBJECTS_NAME "." CAP_OBJECTS__VERB_PREFETCH_NAME "\n" ));
158+ if (err )
159+ return err ;
160+
161+ if (seconds_since_epoch ) {
162+ err = packet_write_fmt_gently (process -> in , "%" PRItime "\n" ,
163+ seconds_since_epoch );
164+ if (err )
165+ return err ;
166+ }
167+
168+ err = packet_flush_gently (process -> in );
169+ if (err )
170+ return err ;
171+
172+ return 0 ;
173+ }
174+
136175/*
137176 * Update the loose object cache to include the newly created
138177 * object.
@@ -181,7 +220,7 @@ static void gh_client__update_packed_git(const char *line)
181220}
182221
183222/*
184- * Both CAP_OBJECTS verbs return the same format response:
223+ * CAP_OBJECTS verbs return the same format response:
185224 *
186225 * <odb>
187226 * <data>*
@@ -221,6 +260,8 @@ static int gh_client__objects__receive_response(
221260 const char * v1 ;
222261 char * line ;
223262 int len ;
263+ int nr_loose = 0 ;
264+ int nr_packfile = 0 ;
224265 int err = 0 ;
225266
226267 while (1 ) {
@@ -239,13 +280,13 @@ static int gh_client__objects__receive_response(
239280 else if (starts_with (line , "packfile" )) {
240281 gh_client__update_packed_git (line );
241282 ghc |= GHC__CREATED__PACKFILE ;
242- * p_nr_packfile += 1 ;
283+ nr_packfile ++ ;
243284 }
244285
245286 else if (starts_with (line , "loose" )) {
246287 gh_client__update_loose_cache (line );
247288 ghc |= GHC__CREATED__LOOSE ;
248- * p_nr_loose += 1 ;
289+ nr_loose ++ ;
249290 }
250291
251292 else if (starts_with (line , "ok" ))
@@ -259,6 +300,8 @@ static int gh_client__objects__receive_response(
259300 }
260301
261302 * p_ghc = ghc ;
303+ * p_nr_loose = nr_loose ;
304+ * p_nr_packfile = nr_packfile ;
262305
263306 return err ;
264307}
@@ -315,7 +358,7 @@ static struct gh_server__process *gh_client__find_long_running_process(
315358 /*
316359 * Find an existing long-running process with the above command
317360 * line -or- create a new long-running process for this and
318- * subsequent 'get' requests.
361+ * subsequent requests.
319362 */
320363 if (!gh_server__subprocess_map_initialized ) {
321364 gh_server__subprocess_map_initialized = 1 ;
@@ -352,10 +395,14 @@ static struct gh_server__process *gh_client__find_long_running_process(
352395
353396void gh_client__queue_oid (const struct object_id * oid )
354397{
355- // TODO consider removing this trace2. it is useful for interactive
356- // TODO debugging, but may generate way too much noise for a data
357- // TODO event.
358- trace2_printf ("gh_client__queue_oid: %s" , oid_to_hex (oid ));
398+ /*
399+ * Keep this trace as a printf only, so that it goes to the
400+ * perf log, but not the event log. It is useful for interactive
401+ * debugging, but generates way too much (unuseful) noise for the
402+ * database.
403+ */
404+ if (trace2_is_enabled ())
405+ trace2_printf ("gh_client__queue_oid: %s" , oid_to_hex (oid ));
359406
360407 if (!oidset_insert (& gh_client__oidset_queued , oid ))
361408 gh_client__oidset_count ++ ;
@@ -436,10 +483,14 @@ int gh_client__get_immediate(const struct object_id *oid,
436483 int nr_packfile = 0 ;
437484 int err = 0 ;
438485
439- // TODO consider removing this trace2. it is useful for interactive
440- // TODO debugging, but may generate way too much noise for a data
441- // TODO event.
442- trace2_printf ("gh_client__get_immediate: %s" , oid_to_hex (oid ));
486+ /*
487+ * Keep this trace as a printf only, so that it goes to the
488+ * perf log, but not the event log. It is useful for interactive
489+ * debugging, but generates way too much (unuseful) noise for the
490+ * database.
491+ */
492+ if (trace2_is_enabled ())
493+ trace2_printf ("gh_client__get_immediate: %s" , oid_to_hex (oid ));
443494
444495 entry = gh_client__find_long_running_process (CAP_OBJECTS );
445496 if (!entry )
@@ -468,3 +519,55 @@ int gh_client__get_immediate(const struct object_id *oid,
468519
469520 return err ;
470521}
522+
523+ /*
524+ * Ask gvfs-helper to prefetch commits-and-trees packfiles since a
525+ * given timestamp.
526+ *
527+ * If seconds_since_epoch is zero, gvfs-helper will scan the ODB for
528+ * the last received prefetch and ask for ones newer than that.
529+ */
530+ int gh_client__prefetch (timestamp_t seconds_since_epoch ,
531+ int * nr_packfiles_received )
532+ {
533+ struct gh_server__process * entry ;
534+ struct child_process * process ;
535+ enum gh_client__created ghc ;
536+ int nr_loose = 0 ;
537+ int nr_packfile = 0 ;
538+ int err = 0 ;
539+
540+ entry = gh_client__find_long_running_process (CAP_OBJECTS );
541+ if (!entry )
542+ return -1 ;
543+
544+ trace2_region_enter ("gh-client" , "objects/prefetch" , the_repository );
545+ trace2_data_intmax ("gh-client" , the_repository , "prefetch/since" ,
546+ seconds_since_epoch );
547+
548+ process = & entry -> subprocess .process ;
549+
550+ sigchain_push (SIGPIPE , SIG_IGN );
551+
552+ err = gh_client__send__objects_prefetch (process , seconds_since_epoch );
553+ if (!err )
554+ err = gh_client__objects__receive_response (
555+ process , & ghc , & nr_loose , & nr_packfile );
556+
557+ sigchain_pop (SIGPIPE );
558+
559+ if (err ) {
560+ subprocess_stop (& gh_server__subprocess_map ,
561+ (struct subprocess_entry * )entry );
562+ FREE_AND_NULL (entry );
563+ }
564+
565+ trace2_data_intmax ("gh-client" , the_repository ,
566+ "prefetch/packfile_count" , nr_packfile );
567+ trace2_region_leave ("gh-client" , "objects/prefetch" , the_repository );
568+
569+ if (nr_packfiles_received )
570+ * nr_packfiles_received = nr_packfile ;
571+
572+ return err ;
573+ }
0 commit comments