janelia-flyem / dvid

Distributed, Versioned, Image-oriented Dataservice
http://dvid.io
Other
196 stars 33 forks source link

Trouble with new /keyvalues endpoint #287

Closed stuarteberg closed 5 years ago

stuarteberg commented 5 years ago

According to the docs, the new /keyvalues?jsontar=true endpoint returns a tar file. But I can't actually open the file it returns.

For example, I can fetch the following key (0+0) via the old /key endpoint:

In [27]: r = requests.get('http://emdata3:8900/api/node/7254/segmentation_merged/key/0+0')

In [28]: r.status_code
Out[28]: 200

In [29]: r.content
Out[29]: b'{"body ID 1":0,"body ID 2":0,"build version":"neu3-0.1.post1261-0_g394ddcf","initial 3D angle method":"method 0 (unchanged from previous task)","result":"autoSkippedSameBody","result history":["autoSkippedSameBody"],"source":"/groups/flyem/data/hemi_brain/whole_hemi_brain/focused-sessions/23722.hopkinsg/23722.hopkinsg.revised.json","supervoxel ID 1":0,"supervoxel ID 2":0,"supervoxel point 1":[0,6.9116409647672219e-310,0],"supervoxel point 2":[6.9116406176011264e-310,6.9116406176011264e-310,6.9116401551572629e-310],"time":"2018-08-16T12:26:25","time to complete (ms)":[],"time zone":"EDT","user":"hopkinsg"}'

So far, so good. But when I attempt to fetch the same key via the /keyvalues?jsontar=true endpoint, the resulting tar file cannot be unpacked:

In [22]: r = requests.get('http://emdata3:8900/api/node/7254/segmentation_merged/keyvalues?jsontar=true', json=['0+0'])

In [23]: r.status_code
Out[23]: 200

In [24]: with open('/tmp/test.tar', 'wb') as f:
    ...:     f.write(r.content)
    ...:

In [25]: !tar -xf /tmp/test.tar
0+0: Truncated input file (needed 412 bytes, only 0 available)
tar: Error exit delayed from previous errors.

In [26]: ls -l /tmp/test.tar
-rw-r--r--  1 bergs  wheel  1124 Aug 23 15:10 /tmp/test.tar

Am I doing something wrong?

DocSavage commented 5 years ago

Hmmm, not that I can see. I have a test case for this endpoint and also added a single key query, both of which works in test. However when doing this example using both python and httpie and evaluating with tar command, I'm getting bad tar files. So looking into it.

DocSavage commented 5 years ago

The issue is the tarfile writer closing was deferred on function exit, which didn't allow full flushing of the buffer when the file was actually written. Oddly, I cannot reproduce the problem in the test suite, presumably because how the test server works vs the production server. I increased the size of payloads into the 10k+ bytes range, started using random bytes instead of constant values, but can't trigger the issue. However it does solve the issue on the production server.

stuarteberg commented 5 years ago

Did you reboot the production server with this fix? I'm still seeing an issue:

In [9]: r = requests.get('http://emdata3:8900/api/node/7254/segmentation_merged/keyvalues?jsontar=true', json=['0+0'])

In [10]: r.status_code
Out[10]: 200

In [11]: open('/tmp/test.tar', 'wb').write(r.content)
Out[11]: 1124

In [12]: !tar -xf /tmp/test.tar
tar: Unexpected EOF in archive
tar: Unexpected EOF in archive
tar: Error is not recoverable: exiting now

Strangely, the file appears to be complete, at least when I inspect the raw bytes in the response:

In [14]: r.content
Out[14]: b'0+0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000755\x000000000\x000000000\x0000000001144\x0000000000000\x00007345\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ustar\x0000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000000\x000000000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00{"body ID 1":0,"body ID 2":0,"build version":"neu3-0.1.post1261-0_g394ddcf","initial 3D angle method":"method 0 (unchanged from previous task)","result":"autoSkippedSameBody","result history":["autoSkippedSameBody"],"source":"/groups/flyem/data/hemi_brain/whole_hemi_brain/focused-sessions/23722.hopkinsg/23722.hopkinsg.revised.json","supervoxel ID 1":0,"supervoxel ID 2":0,"supervoxel point 1":[0,6.9116409647672219e-310,0],"supervoxel point 2":[6.9116406176011264e-310,6.9116406176011264e-310,6.9116401551572629e-310],"time":"2018-08-16T12:26:25","time to complete (ms)":[],"time zone":"EDT","user":"hopkinsg"}'

(If you scroll to the end of that, you can see that the entire JSON content is present.)

DocSavage commented 5 years ago

The main production server wasn't rebooted yet. This fix worked on the emdata2:7900, a replica of the production server. The test server I mention is the unit test server built into Go. I believe the http test server in Go has different buffering dynamics so a tar Writer flush isn't necessary whereas it would be for real http servers.