Set backend content length for fallocate - EC Policy
Currently, the ECObjectController removes the 'content-length' header. This part is ok, except that value is being used to set 'X-Backend-Obj-Content-Length', so it is always 0. This leads to not calling fallocate (details on bug) on a PUT since the size is 0. This change makes use of some numbers returned from the EC Driver get_segment_info method in order to calculate the expected on-disk size that should be allocated. The EC controller will now set the 'X-Backend-Obj-Content-Length' value appropriately. Co-Authored-By: Kota Tsuyuzaki Co-Authored-By: John Dickinson Co-Authored-By: Tim Burke Change-Id: Ifd16c1438539e6fd9bb2dbcd053d11bea2e09fee Fixes: bug 1532008
This commit is contained in:
parent
99bc2a4a7d
commit
b5a243e75a
@ -1769,7 +1769,7 @@ class ECPutter(object):
|
||||
|
||||
@classmethod
|
||||
def connect(cls, node, part, path, headers, conn_timeout, node_timeout,
|
||||
chunked=False):
|
||||
chunked=False, expected_frag_archive_size=None):
|
||||
"""
|
||||
Connect to a backend node and send the headers.
|
||||
|
||||
@ -1791,9 +1791,10 @@ class ECPutter(object):
|
||||
# we must use chunked encoding.
|
||||
headers['Transfer-Encoding'] = 'chunked'
|
||||
headers['Expect'] = '100-continue'
|
||||
if 'Content-Length' in headers:
|
||||
headers['X-Backend-Obj-Content-Length'] = \
|
||||
headers.pop('Content-Length')
|
||||
|
||||
# make sure this isn't there
|
||||
headers.pop('Content-Length')
|
||||
headers['X-Backend-Obj-Content-Length'] = expected_frag_archive_size
|
||||
|
||||
headers['X-Backend-Obj-Multipart-Mime-Boundary'] = mime_boundary
|
||||
|
||||
@ -2105,16 +2106,41 @@ class ECObjectController(BaseObjectController):
|
||||
# the object server will get different bytes, so these
|
||||
# values do not apply (Content-Length might, in general, but
|
||||
# in the specific case of replication vs. EC, it doesn't).
|
||||
headers.pop('Content-Length', None)
|
||||
client_cl = headers.pop('Content-Length', None)
|
||||
headers.pop('Etag', None)
|
||||
|
||||
expected_frag_size = None
|
||||
if client_cl:
|
||||
policy_index = int(headers.get('X-Backend-Storage-Policy-Index'))
|
||||
policy = POLICIES.get_by_index(policy_index)
|
||||
# TODO: PyECLib <= 1.2.0 looks to return the segment info
|
||||
# different from the input for aligned data efficiency but
|
||||
# Swift never does. So calculate the fragment length Swift
|
||||
# will actually send to object sever by making two different
|
||||
# get_segment_info calls (until PyECLib fixed).
|
||||
# policy.fragment_size makes the call using segment size,
|
||||
# and the next call is to get info for the last segment
|
||||
|
||||
# get number of fragments except the tail - use truncation //
|
||||
num_fragments = int(client_cl) // policy.ec_segment_size
|
||||
expected_frag_size = policy.fragment_size * num_fragments
|
||||
|
||||
# calculate the tail fragment_size by hand and add it to
|
||||
# expected_frag_size
|
||||
last_segment_size = int(client_cl) % policy.ec_segment_size
|
||||
if last_segment_size:
|
||||
last_info = policy.pyeclib_driver.get_segment_info(
|
||||
last_segment_size, policy.ec_segment_size)
|
||||
expected_frag_size += last_info['fragment_size']
|
||||
|
||||
self.app.logger.thread_locals = logger_thread_locals
|
||||
for node in node_iter:
|
||||
try:
|
||||
putter = ECPutter.connect(
|
||||
node, part, path, headers,
|
||||
conn_timeout=self.app.conn_timeout,
|
||||
node_timeout=self.app.node_timeout)
|
||||
node_timeout=self.app.node_timeout,
|
||||
expected_frag_archive_size=expected_frag_size)
|
||||
self.app.set_node_timing(node, putter.connect_duration)
|
||||
return putter
|
||||
except InsufficientStorage:
|
||||
|
@ -1483,6 +1483,8 @@ class TestECObjController(BaseObjectControllerMixin, unittest.TestCase):
|
||||
conn_id = kwargs['connection_id']
|
||||
put_requests[conn_id]['boundary'] = headers[
|
||||
'X-Backend-Obj-Multipart-Mime-Boundary']
|
||||
put_requests[conn_id]['backend-content-length'] = headers[
|
||||
'X-Backend-Obj-Content-Length']
|
||||
|
||||
with set_http_connect(*codes, expect_headers=expect_headers,
|
||||
give_send=capture_body,
|
||||
@ -1496,6 +1498,9 @@ class TestECObjController(BaseObjectControllerMixin, unittest.TestCase):
|
||||
self.assertTrue(info['boundary'] is not None,
|
||||
"didn't get boundary for conn %r" % (
|
||||
connection_id,))
|
||||
self.assertTrue(size > int(info['backend-content-length']) > 0,
|
||||
"invalid backend-content-length for conn %r" % (
|
||||
connection_id,))
|
||||
|
||||
# email.parser.FeedParser doesn't know how to take a multipart
|
||||
# message and boundary together and parse it; it only knows how
|
||||
@ -1517,6 +1522,13 @@ class TestECObjController(BaseObjectControllerMixin, unittest.TestCase):
|
||||
self.assertEqual(obj_part['X-Document'], 'object body')
|
||||
frag_archives.append(obj_part.get_payload())
|
||||
|
||||
# assert length was correct for this connection
|
||||
self.assertEqual(int(info['backend-content-length']),
|
||||
len(frag_archives[-1]))
|
||||
# assert length was the same for all connections
|
||||
self.assertEqual(int(info['backend-content-length']),
|
||||
len(frag_archives[0]))
|
||||
|
||||
# validate some footer metadata
|
||||
self.assertEqual(footer_part['X-Document'], 'object metadata')
|
||||
footer_metadata = json.loads(footer_part.get_payload())
|
||||
|
Loading…
x
Reference in New Issue
Block a user