git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [RFC/PATCH v3 01/16] Add initial external odb support

From
Jeff King <peff@peff.net>
Date
Aug 3, 2017, 08:06 UTC
Message-ID
<20170803080600.aeaprj5hb6ucrkgy@sigill.intra.peff.net>
In-Reply-To
<CAP8UFD0ecFW2Sk0fr3ysAXPERNp1RiBMqZMTjYxgt_mvtY-kaw@mail.gmail.com>
On Thu, Aug 03, 2017 at 09:46:38AM +0200, Christian Couder wrote:
Show 15 quoted lines
> >> +static int external_odb_config(const char *var, const char *value, void *data)
> >> +{
> >> +     struct odb_helper *o;
> >> +     const char *key, *dot;
> >> +
> >> +     if (!skip_prefix(var, "odb.", &key))
> >> +             return 0;
> >> +     dot = strrchr(key, '.');
> >> +     if (!dot)
> >> +             return 0;
> >
> > Is this something Peff wrote long time ago?  I find it surprising
> > that he would write this without using parse_config_key().
> 
> parse_config_key() is used now.

Yeah, I think the original was from 2012. We didn't add parse_config_key() until 2013. Definitely worth using.

Show 50 quoted lines
> >> +     for (;;) {
> >> +             unsigned char buf[4096];
> >> +             int r;
> >> +
> >> +             r = xread(cmd.child.out, buf, sizeof(buf));
> >> +             if (r < 0) {
> >> +                     error("unable to read from odb helper '%s': %s",
> >> +                           o->name, strerror(errno));
> >> +                     close(cmd.child.out);
> >> +                     odb_helper_finish(o, &cmd);
> >> +                     git_inflate_end(&stream);
> >> +                     return -1;
> >> +             }
> >> +             if (r == 0)
> >> +                     break;
> >> +
> >> +             write_or_die(fd, buf, r);
> >> +
> >> +             stream.next_in = buf;
> >> +             stream.avail_in = r;
> >> +             do {
> >> +                     unsigned char inflated[4096];
> >> +                     unsigned long got;
> >> +
> >> +                     stream.next_out = inflated;
> >> +                     stream.avail_out = sizeof(inflated);
> >> +                     zret = git_inflate(&stream, Z_SYNC_FLUSH);
> >> +                     got = sizeof(inflated) - stream.avail_out;
> >> +
> >> +                     git_SHA1_Update(&hash, inflated, got);
> >> +                     /* skip header when counting size */
> >> +                     if (!total_got) {
> >> +                             const unsigned char *p = memchr(inflated, '\0', got);
> [reconstructed quoted function so we can see the whole thing]
> >> +                             if (p)
> >> +                                     got -= p - inflated + 1;
> >> +                             else
> >> +                                     got = 0;
> >> +                     }
> >> +                     total_got += got;
> >> +             } while (stream.avail_in && zret == Z_OK);
> >> +     }
> >
> > Does this assume that a single xread() that can result in a
> > short-read would not return in the middle of "header", and if so, is
> > that a safe assumption to make?
> 
> I am not sure what would go wrong in case of a short read.
> My guess is that as long as we test that p is not NULL below we should be fine.
> As Peff wrote this code, he could probably answer much better than me.

I think it's OK. The idea is to suck up characters until we hit the end-of-header NUL. So "total_got" only becomes non-zero once we've seen that NUL. If we get a short read then that memchr() returns NULL, and we set "got" to 0, and don't advance total_got at all. When we finally do hit a partial read that contains the NUL, then "p" will be non-NULL, and we'll reduce "got" as appropriate.

All that said, I agree with the bits you both said later that we should probably be checking the actual content of the header (if we're indeed going to keep this on-the-wire format -- see below).

Show 10 quoted lines
> > I am tempted to debate myself if it is a sensible design to require
> > "get" to return a loose object representation, but cannot decide
> > without seeing the remainder of the series.  An obvious alternative
> > is to define the "get" request to interface with us via the raw
> > contents (not even deflated) and leave the deflating to us, i.e. Git
> > sitting on the receiving end, which would allow us to choose to
> > store it differently (e.g. we may want to try streaming it into its
> > own pack using the streaming.h API, for example).
> 
> There is now both a get_raw_obj and a get_git_obj to handle both cases.

Yeah, I don't recall why I picked the loose format as the transfer mechanism. I guess I figured that the objects would be large, so we'd want them in their own loose objects (not part of a pack that might have to get rewritten). So storing and sending as a loose-object zlib stream means we can avoid any recompression and just send it out to disk.

But that was a long time ago. I think these days we prefer to put even large objects into packfiles. If we wanted something the client could stream directly into storage, the pack format would probably make more sense. But it also probably isn't the end of the world to just get raw contents and then re-zlib them. That's a little extra overhead on the receiving side, but it makes things nice and simple. And we're already uncompressing and computing the sha1 to verify the incoming content anyway.

-Peff
Previous: Christian CouderNext: Christian Couder
Message 12 of 30 in “Add initial experimental external ODB support”
  1. 00/16 Add initial experimental external ODB supportChristian Couder, Nov 30, 2016
  2. 05/16 t0400: add test for 'put' commandChristian Couder, Nov 30, 2016
  3. 06/16 external odb: add write supportChristian Couder, Nov 30, 2016
  4. 16/16 t0420: add test with HTTP external odbChristian Couder, Nov 30, 2016
  5. 15/16 odb-helper: add 'store_plain_objects' to 'struct odb_helper'Christian Couder, Nov 30, 2016
  6. 14/16 lib-httpd: add apache-e-odb.confChristian Couder, Nov 30, 2016
  7. 01/16 Add initial external odb supportChristian Couder, Nov 30, 2016
  8. Junio C HamanoNov 30, 2016
  9. Jeff KingNov 30, 2016
  10. Christian CouderAug 3, 2017
  11. Christian CouderAug 3, 2017
  12. Jeff KingAug 3, 2017
  13. 09/16 Add GIT_NO_EXTERNAL_ODB env variableChristian Couder, Nov 30, 2016
  14. 10/16 Add t0410 to test external ODB transferChristian Couder, Nov 30, 2016
  15. 13/16 lib-httpd: add list.shChristian Couder, Nov 30, 2016
  16. 11/16 lib-httpd: pass config file to start_httpd()Christian Couder, Nov 30, 2016
  17. 07/16 external-odb: accept only blobs for nowChristian Couder, Nov 30, 2016
  18. 03/16 t0400: use --batch-all-objects to get all objectsChristian Couder, Nov 30, 2016
  19. 02/16 external odb foreachChristian Couder, Nov 30, 2016
  20. 12/16 lib-httpd: add upload.shChristian Couder, Nov 30, 2016
  21. Junio C HamanoNov 30, 2016
  22. Christian CouderDec 13, 2016
  23. Junio C HamanoDec 13, 2016
  24. Christian CouderDec 15, 2016
  25. 04/16 t0400: add 'put' command to odb-helper scriptChristian Couder, Nov 30, 2016
  26. 08/16 t0400: add test for external odb write supportChristian Couder, Nov 30, 2016
  27. Lars SchneiderDec 3, 2016
  28. Jeff KingDec 5, 2016
  29. Christian CouderDec 13, 2016
  30. Lars SchneiderDec 18, 2016

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.