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>