Skip to content

Commit ee0e3bc

Browse files
authored
Dynamic chunk size (#122)
For chunked uploads, choose chunk size based on * `_RESPONSE_TIME_MAX` * `x-response-time`
1 parent 09165df commit ee0e3bc

File tree

3 files changed

+39
-6
lines changed

3 files changed

+39
-6
lines changed

tests/test_utils.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import datetime
2+
from random import randint
23

34
from twitter_ads.enum import GRANULARITY
4-
from twitter_ads.utils import to_time
5+
from twitter_ads.utils import size, to_time
56

67

78
t = datetime.datetime(2006, 3, 21, 0, 0, 0)
@@ -10,3 +11,18 @@ def test_to_time_based_on_granularity():
1011
for g in [None, GRANULARITY.HOUR, GRANULARITY.TOTAL]:
1112
assert to_time(t, g) == '2006-03-21T00:00:00Z'
1213
assert to_time(t, GRANULARITY.DAY) == '2006-03-21'
14+
15+
def test_sizes():
16+
_DEFAULT_CHUNK_SIZE = 64
17+
_RESPONSE_TIME_MAX = 5000
18+
for _ in range(10):
19+
response_time = randint(0, _RESPONSE_TIME_MAX)
20+
assert size(_DEFAULT_CHUNK_SIZE, _RESPONSE_TIME_MAX, response_time) == _DEFAULT_CHUNK_SIZE
21+
response_times = {10000 : 32,
22+
20000 : 16,
23+
40000 : 8,
24+
80000 : 4,
25+
160000 : 2,
26+
320000 : 1}
27+
for rt in response_times:
28+
assert size(_DEFAULT_CHUNK_SIZE, _RESPONSE_TIME_MAX, rt) == response_times[rt]

twitter_ads/http.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
from requests_oauthlib import OAuth1Session
2121

22-
from twitter_ads.utils import get_version, http_time
22+
from twitter_ads.utils import get_version, http_time, size
2323
from twitter_ads.error import Error
2424

2525

@@ -195,7 +195,9 @@ class TONUpload(object):
195195
_DEFAULT_RESOURCE = '/1.1/ton/bucket/'
196196
_DEFAULT_BUCKET = 'ta_partner'
197197
_DEFAULT_EXPIRE = datetime.now() + timedelta(days=10)
198-
_MIN_FILE_SIZE = 1024 * 1024 * 64
198+
_DEFAULT_CHUNK_SIZE = 64
199+
_SINGLE_UPLOAD_MAX = 1024 * 1024 * _DEFAULT_CHUNK_SIZE
200+
_RESPONSE_TIME_MAX = 5000
199201

200202
def __init__(self, client, file_path, **kwargs):
201203
if not os.path.isfile(file_path):
@@ -240,13 +242,14 @@ def content_type(self):
240242
def perform(self):
241243
"""Executes the current TONUpload object."""
242244

243-
if self._file_size < self._MIN_FILE_SIZE:
245+
if self._file_size < self._SINGLE_UPLOAD_MAX:
244246
resource = "{0}{1}".format(self._DEFAULT_RESOURCE, self.bucket)
245247
response = self.__upload(resource, open(self._file_path, 'rb').read())
246248
return response.headers['location']
247249
else:
248250
response = self.__init_chunked_upload()
249-
chunk_size = int(response.headers['x-ton-min-chunk-size'])
251+
min_chunk_size = int(response.headers['x-ton-min-chunk-size'])
252+
chunk_size = min_chunk_size * self._DEFAULT_CHUNK_SIZE
250253
location = response.headers['location']
251254

252255
f = open(self._file_path, 'rb')
@@ -257,7 +260,11 @@ def perform(self):
257260
break
258261
bytes_start = bytes_read
259262
bytes_read += len(bytes)
260-
self.__upload_chunk(location, chunk_size, bytes, bytes_start, bytes_read)
263+
response = self.__upload_chunk(location, chunk_size, bytes, bytes_start, bytes_read)
264+
response_time = int(response.headers['x-response-time'])
265+
chunk_size = min_chunk_size * size(self._DEFAULT_CHUNK_SIZE,
266+
self._RESPONSE_TIME_MAX,
267+
response_time)
261268
f.close()
262269

263270
return location.split("?")[0]

twitter_ads/utils.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Copyright (C) 2015 Twitter, Inc.
2+
from __future__ import division
23

34
"""Container for all helpers and utilities used throughout the Ads API SDK."""
45

@@ -45,3 +46,12 @@ def format_date(time):
4546
def http_time(time):
4647
"""Formats a datetime as an RFC 1123 compliant string."""
4748
return formatdate(timeval=mktime(time.timetuple()), localtime=False, usegmt=True)
49+
50+
51+
def size(default_chunk_size, response_time_max, response_time_actual):
52+
"""Determines the chunk size based on response times."""
53+
if response_time_actual == 0:
54+
response_time_actual = 1
55+
scale = 1 / (response_time_actual / response_time_max)
56+
size = int(default_chunk_size * scale)
57+
return min(max(size, 1), default_chunk_size)

0 commit comments

Comments
 (0)