server: Cache per-connection size
commitb9d4875ba7bb2d9ac24a080f25f308b23a0e48e3
authorEric Blake <eblake@redhat.com>
Wed, 28 Aug 2019 15:33:42 +0000 (28 10:33 -0500)
committerEric Blake <eblake@redhat.com>
Fri, 30 Aug 2019 15:26:44 +0000 (30 10:26 -0500)
tree8f4575c1a76725be1586bca90a568d30c0484cf7
parentf7136f5ae722eb17125f01ab784a685c79e9e398
server: Cache per-connection size

We don't know how long a plugin's .get_size() will take, but we also
documented that it shouldn't change per connection and therefore can
be cached.  It's not hard to see that we have to consult the size per
connection (see commit b3a43ccd for a test that purposefully exposes
different sizes to separate clients), nor to search the code to see we
already cache it at the protocol level, given that each .extents call
uses nbdkit_extents_new() clamped to the cached size:

$ cat script
case "$1" in
    get_size) sleep 1; echo 1m;;
    can_extents) ;;
    extents) echo 0 1m;;
    *) exit 2 ;;
esac
$ /bin/time -f %e \
  ./nbdkit -U - sh script --run 'qemu-io -r -f raw -c map -c map $nbd'
1 MiB (0x100000) bytes     allocated at offset 0 bytes (0x0)
1 MiB (0x100000) bytes     allocated at offset 0 bytes (0x0)
1.08

Here, we saw completion in just over a second, so the sleep in
get_size was called only once.  But we are not caching it in all
filters:

$ /bin/time -f %e \
  ./nbdkit -U - --filter=offset sh script offset=512k \
  --run 'qemu-io -r -f raw -c map -c map $nbd'
512 KiB (0x80000) bytes     allocated at offset 0 bytes (0x0)
512 KiB (0x80000) bytes     allocated at offset 0 bytes (0x0)
3.13

Oops; the 3 second delay demonstrates that the offset filter calls
next->get_size() once per top-level .extents call.

The previous patches made it easy to support a framework for
per-backend caching, so now to put it to use by remembering the result
of get_size() for each.  With that, the above example with offset
filtering speeds up to a completion in just over one second.

Signed-off-by: Eric Blake <eblake@redhat.com>
docs/nbdkit-filter.pod
server/backend.c
server/connections.c
server/internal.h
server/protocol-handshake-newstyle.c
server/protocol-handshake-oldstyle.c
server/protocol.c