summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJanie Richling <jrichli@us.ibm.com>2016-02-19 18:07:18 -0600
committerJanie Richling <jrichli@us.ibm.com>2016-02-22 15:27:53 -0600
commitb5a243e75a8033988063d2c1c90ac373bc0050d2 (patch)
tree3a12b7c46a72c69cf98ae82973b31301e3eac6b3
parent99bc2a4a7d75f06b62908388e8890af2d7486085 (diff)
downloadswift-b5a243e75a8033988063d2c1c90ac373bc0050d2.tar.gz
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
-rw-r--r--swift/proxy/controllers/obj.py38
-rwxr-xr-xtest/unit/proxy/controllers/test_obj.py12
2 files changed, 44 insertions, 6 deletions
diff --git a/swift/proxy/controllers/obj.py b/swift/proxy/controllers/obj.py
index f3c13d589..9b5756725 100644
--- a/swift/proxy/controllers/obj.py
+++ b/swift/proxy/controllers/obj.py
@@ -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:
diff --git a/test/unit/proxy/controllers/test_obj.py b/test/unit/proxy/controllers/test_obj.py
index c39cd1950..ea4f6d150 100755
--- a/test/unit/proxy/controllers/test_obj.py
+++ b/test/unit/proxy/controllers/test_obj.py
@@ -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())