[Libguestfs] [PATCH disk-sync 5/5] Convert disk_sync inner loop to asynchronous.

Martin Kletzander mkletzan at redhat.com
Tue Aug 27 14:49:29 UTC 2019


On Tue, Aug 27, 2019 at 10:00:59AM +0100, Richard W.M. Jones wrote:
>On Mon, Aug 26, 2019 at 01:36:41PM +0200, Martin Kletzander wrote:
>> On Thu, Aug 22, 2019 at 03:39:35PM +0100, Richard W.M. Jones wrote:
>> >Previously the inner loop would issue nbd.pread() requests
>> >synchronously, meaning that we would issue a request for each data
>> >block from the nbdkit server (which would in turn synchronously
>> >request the data from VMware) and wait until nbdkit replies before
>> >continuing.
>> >
>> >This converts the inner loop so it issues as many pread requests
>> >asychronously to nbdkit as the server can handle (any extra are queued
>> >in nbd_handle).  The server will answer these in parallel and possibly
>> >out of order.
>> >
>> >This results in somewhat better throughput (for me: 13 minutes down to
>> >5 minutes for an "initial" run).  Although unfortunately we are still
>> >limited by VDDK's braindead threading model.
>> >---
>> >wrapper/disk_sync.py | 55 +++++++++++++++++++++++++++++++-------------
>> >1 file changed, 39 insertions(+), 16 deletions(-)
>> >
>> >diff --git a/wrapper/disk_sync.py b/wrapper/disk_sync.py
>> >index e655ead..e854009 100644
>> >--- a/wrapper/disk_sync.py
>> >+++ b/wrapper/disk_sync.py
>> >@@ -409,6 +409,26 @@ def get_block_status(nbd_handle, extent):
>> >    return blocks
>> >
>> >
>> >+# This is called back when nbd_aio_pread completes.
>> >+def read_completed(fd, buf, offset, err):
>> >+    logging.debug('Writing %d B to offset %d B' % (buf.size(), offset))
>> >+    os.pwrite(fd, buf.to_bytearray(), offset)
>> >+    # By returning 1 here we auto-retire the aio_pread command.
>> >+    return 1
>> >+
>> >+
>> >+# Process any AIO requests without blocking.
>> >+def process_aio_requests(nbd_handle):
>> >+    while nbd_handle.poll(0) == 1:
>> >+        pass
>> >+
>> >+
>> >+# Block until all AIO commands on the handle have finished.
>> >+def wait_for_aio_commands_to_finish(nbd_handle):
>> >+    while nbd_handle.aio_in_flight() > 0:
>> >+        nbd_handle.poll(-1)
>> >+
>> >+
>> >def sync_data():
>> >    state = State().instance
>> >    for key, disk in state['disks'].items():
>> >@@ -491,25 +511,28 @@ def sync_data():
>> >                              (block['length'], block['offset']))
>> >                # Optimize for memory usage, maybe?
>> >                os.pwrite(fd, [0] * block['length'], block['offset'])
>> >-                copied += block['length']
>> >-                disk['progress']['copied'] = copied
>> >-                state.write()
>> >            else:
>> >-                wrote = 0
>> >-                while wrote < block['length']:
>> >-                    length = min(block['length'] - wrote, MAX_PREAD_LEN)
>> >-                    offset = block['offset'] + wrote
>> >+                count = 0
>> >+                while count < block['length']:
>> >+                    length = min(block['length'] - count, MAX_PREAD_LEN)
>> >+                    offset = block['offset'] + count
>> >+
>> >                    logging.debug('Reading %d B from offset %d B' %
>> >                                  (length, offset))
>> >-                    # Ideally use mmap() without any temporary buffer
>> >-                    data = nbd_handle.pread(length, offset)
>> >-                    logging.debug('Writing %d B to offset %d B' %
>> >-                                  (length, offset))
>> >-                    os.pwrite(fd, data, offset)
>> >-                    copied += length
>> >-                    wrote += length
>> >-                    disk['progress']['copied'] = copied
>> >-                    state.write()
>> >+                    buf = nbd.Buffer(length)
>> >+                    nbd_handle.aio_pread(
>> >+                        buf, offset,
>> >+                        lambda err, fd=fd, buf=buf, offset=offset:
>> >+                        read_completed(fd, buf, offset, err))
>>
>> If the order of parameters is changed, there is no need for the anonymous
>> function here, but that's just a small thing I noticed.
>
>The initialized parameters seem to be needed to work around a bug
>(more properly "serious implementation flaw") in Python:
>
>https://stackoverflow.com/questions/2295290
>

Oh, wow, that's really good to know.

>> >+                    count += length
>> >+
>> >+                    process_aio_requests(nbd_handle)
>>
>> In order to allow less requests in flight, would it be enough to just do
>> something like this here (similarly to wait_for_aio_commands_to_finish)?
>>
>>    while nbd_handle.aio_in_flight() > NUM_IN_FLIGHT:
>>        nbd_handle.poll(-1)
>
>Yes we could do this.
>
>The eventual solution may be to replace the whole loop with a polling
>loop, but this would be a more invasive change.  For comparison of
>this approach see:
>
>https://github.com/libguestfs/libnbd/blob/7ef893735937cd7ae62d0b41171ec14195ef2710/examples/threaded-reads-and-writes.c#L234-L307
>
>> Also, I presume all of the locking is left to libnbd to be done (and
>> as you can see I don't concern myself with any locking in the whole
>> file), but if that was to be improved, is there some python part
>> that would require it?  For example when cleaning up the code?
>
>There's a lock automatically held whenever any method on nbd_handle is
>called.  I don't believe any other locking is needed.  In any case
>it's all single-threaded, libnbd does not create any threads itself.
>
>Rich.
>
>-- 
>Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
>Read my programming and virtualization blog: http://rwmj.wordpress.com
>virt-df lists disk usage of guests without needing to install any
>software inside the virtual machine.  Supports Linux and Windows.
>http://people.redhat.com/~rjones/virt-df/
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/libguestfs/attachments/20190827/b219dbfe/attachment.sig>


More information about the Libguestfs mailing list