I am trying to permit a python app to access various locations in a many-GB file stored in S3. I’d like to create a drop-in replacement file-like object that intelligently downloads chunks of data from S3 in a separate thread to meet seek() and read() requests.
Is there a simple data structure I can use to store arbitrary intervals of the file?
It must support O(log n) look-up and O(n) insertion (n=number of chunks, not size of file). It will also need to support quickly querying for gaps so that the load thread can efficiently find the next chunk it should download. This is currently not supported by things like SortedCollection, suggesting I may need to manually use bisect_* in a new container.
Example usage is:
import os
import time
from bigfile import BigFile
chunksize = (2**20)*64 # 64MB
bf = BigFile('my_bucket', 'key_name', chunksize=chunksize)
# read from beginning (blocks until first chunk arrives)
bf.read(100)
# continues downloading subsequent chunks in background
time.sleep(10)
# seek into second chunk and read (should not block)
bf.seek(blocksize, os.SEEK_SET)
bf.read(100)
# seek far into the file
bf.seek(blocksize*100 + 54, os.SEEK_SET) # triggers chunk download starting at new location
bf.read(100) # blocks until chunk arrives
# seek back to beginning (should not block, already have this chunk)
bf.seek(0, os.SEEK_SET)
bf.read(100)
# read entire rest of file (blocks until all chunks are downloaded)
bf.read()
This implementation uses chunks of fixed size and offsets. If the chunks are very large and the network is very slow, reads may block for a long time (consider a read starting at the last byte of a chunk, it would have to wait for the entire previous chunk to load, then the next chunk).
Ideally we could use chunks of arbitrary size and location, so we can optimize loads to start at exactly the read point. But below is a good 80% solution.