swift 2.23.3__py3-none-any.whl → 2.35.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- swift/__init__.py +29 -50
- swift/account/auditor.py +21 -118
- swift/account/backend.py +33 -28
- swift/account/reaper.py +37 -28
- swift/account/replicator.py +22 -0
- swift/account/server.py +60 -26
- swift/account/utils.py +28 -11
- swift-2.23.3.data/scripts/swift-account-audit → swift/cli/account_audit.py +23 -13
- swift-2.23.3.data/scripts/swift-config → swift/cli/config.py +2 -2
- swift/cli/container_deleter.py +5 -11
- swift-2.23.3.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +8 -7
- swift/cli/dispersion_report.py +10 -9
- swift-2.23.3.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +63 -21
- swift/cli/form_signature.py +3 -7
- swift-2.23.3.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +8 -2
- swift/cli/info.py +154 -14
- swift/cli/manage_shard_ranges.py +705 -37
- swift-2.23.3.data/scripts/swift-oldies → swift/cli/oldies.py +25 -14
- swift-2.23.3.data/scripts/swift-orphans → swift/cli/orphans.py +7 -3
- swift/cli/recon.py +196 -67
- swift-2.23.3.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +17 -20
- swift-2.23.3.data/scripts/swift-reconciler-enqueue → swift/cli/reconciler_enqueue.py +2 -3
- swift/cli/relinker.py +807 -126
- swift/cli/reload.py +135 -0
- swift/cli/ringbuilder.py +217 -20
- swift/cli/ringcomposer.py +0 -1
- swift/cli/shard-info.py +4 -3
- swift/common/base_storage_server.py +9 -20
- swift/common/bufferedhttp.py +48 -74
- swift/common/constraints.py +20 -15
- swift/common/container_sync_realms.py +9 -11
- swift/common/daemon.py +25 -8
- swift/common/db.py +195 -128
- swift/common/db_auditor.py +168 -0
- swift/common/db_replicator.py +95 -55
- swift/common/digest.py +141 -0
- swift/common/direct_client.py +144 -33
- swift/common/error_limiter.py +93 -0
- swift/common/exceptions.py +25 -1
- swift/common/header_key_dict.py +2 -9
- swift/common/http_protocol.py +373 -0
- swift/common/internal_client.py +129 -59
- swift/common/linkat.py +3 -4
- swift/common/manager.py +284 -67
- swift/common/memcached.py +390 -145
- swift/common/middleware/__init__.py +4 -0
- swift/common/middleware/account_quotas.py +211 -46
- swift/common/middleware/acl.py +3 -8
- swift/common/middleware/backend_ratelimit.py +230 -0
- swift/common/middleware/bulk.py +22 -34
- swift/common/middleware/catch_errors.py +1 -3
- swift/common/middleware/cname_lookup.py +6 -11
- swift/common/middleware/container_quotas.py +1 -1
- swift/common/middleware/container_sync.py +39 -17
- swift/common/middleware/copy.py +12 -0
- swift/common/middleware/crossdomain.py +22 -9
- swift/common/middleware/crypto/__init__.py +2 -1
- swift/common/middleware/crypto/crypto_utils.py +11 -15
- swift/common/middleware/crypto/decrypter.py +28 -11
- swift/common/middleware/crypto/encrypter.py +12 -17
- swift/common/middleware/crypto/keymaster.py +8 -15
- swift/common/middleware/crypto/kms_keymaster.py +2 -1
- swift/common/middleware/dlo.py +15 -11
- swift/common/middleware/domain_remap.py +5 -4
- swift/common/middleware/etag_quoter.py +128 -0
- swift/common/middleware/formpost.py +73 -70
- swift/common/middleware/gatekeeper.py +8 -1
- swift/common/middleware/keystoneauth.py +33 -3
- swift/common/middleware/list_endpoints.py +4 -4
- swift/common/middleware/listing_formats.py +85 -49
- swift/common/middleware/memcache.py +4 -95
- swift/common/middleware/name_check.py +3 -2
- swift/common/middleware/proxy_logging.py +160 -92
- swift/common/middleware/ratelimit.py +17 -10
- swift/common/middleware/read_only.py +6 -4
- swift/common/middleware/recon.py +59 -22
- swift/common/middleware/s3api/acl_handlers.py +25 -3
- swift/common/middleware/s3api/acl_utils.py +6 -1
- swift/common/middleware/s3api/controllers/__init__.py +6 -0
- swift/common/middleware/s3api/controllers/acl.py +3 -2
- swift/common/middleware/s3api/controllers/bucket.py +242 -137
- swift/common/middleware/s3api/controllers/logging.py +2 -2
- swift/common/middleware/s3api/controllers/multi_delete.py +43 -20
- swift/common/middleware/s3api/controllers/multi_upload.py +219 -133
- swift/common/middleware/s3api/controllers/obj.py +112 -8
- swift/common/middleware/s3api/controllers/object_lock.py +44 -0
- swift/common/middleware/s3api/controllers/s3_acl.py +2 -2
- swift/common/middleware/s3api/controllers/tagging.py +57 -0
- swift/common/middleware/s3api/controllers/versioning.py +36 -7
- swift/common/middleware/s3api/etree.py +22 -9
- swift/common/middleware/s3api/exception.py +0 -4
- swift/common/middleware/s3api/s3api.py +113 -41
- swift/common/middleware/s3api/s3request.py +384 -218
- swift/common/middleware/s3api/s3response.py +126 -23
- swift/common/middleware/s3api/s3token.py +16 -17
- swift/common/middleware/s3api/schema/delete.rng +1 -1
- swift/common/middleware/s3api/subresource.py +7 -10
- swift/common/middleware/s3api/utils.py +27 -10
- swift/common/middleware/slo.py +665 -358
- swift/common/middleware/staticweb.py +64 -37
- swift/common/middleware/symlink.py +51 -18
- swift/common/middleware/tempauth.py +76 -58
- swift/common/middleware/tempurl.py +191 -173
- swift/common/middleware/versioned_writes/__init__.py +51 -0
- swift/common/middleware/{versioned_writes.py → versioned_writes/legacy.py} +27 -26
- swift/common/middleware/versioned_writes/object_versioning.py +1482 -0
- swift/common/middleware/x_profile/exceptions.py +1 -4
- swift/common/middleware/x_profile/html_viewer.py +18 -19
- swift/common/middleware/x_profile/profile_model.py +1 -2
- swift/common/middleware/xprofile.py +10 -10
- swift-2.23.3.data/scripts/swift-container-server → swift/common/recon.py +13 -8
- swift/common/registry.py +147 -0
- swift/common/request_helpers.py +324 -57
- swift/common/ring/builder.py +67 -25
- swift/common/ring/composite_builder.py +1 -1
- swift/common/ring/ring.py +177 -51
- swift/common/ring/utils.py +1 -1
- swift/common/splice.py +10 -6
- swift/common/statsd_client.py +205 -0
- swift/common/storage_policy.py +49 -44
- swift/common/swob.py +86 -102
- swift/common/{utils.py → utils/__init__.py} +2163 -2772
- swift/common/utils/base.py +131 -0
- swift/common/utils/config.py +433 -0
- swift/common/utils/ipaddrs.py +256 -0
- swift/common/utils/libc.py +345 -0
- swift/common/utils/logs.py +859 -0
- swift/common/utils/timestamp.py +412 -0
- swift/common/wsgi.py +553 -535
- swift/container/auditor.py +14 -100
- swift/container/backend.py +490 -231
- swift/container/reconciler.py +126 -37
- swift/container/replicator.py +96 -22
- swift/container/server.py +358 -165
- swift/container/sharder.py +1540 -684
- swift/container/sync.py +94 -88
- swift/container/updater.py +53 -32
- swift/obj/auditor.py +153 -35
- swift/obj/diskfile.py +466 -217
- swift/obj/expirer.py +406 -124
- swift/obj/mem_diskfile.py +7 -4
- swift/obj/mem_server.py +1 -0
- swift/obj/reconstructor.py +523 -262
- swift/obj/replicator.py +249 -188
- swift/obj/server.py +207 -122
- swift/obj/ssync_receiver.py +145 -85
- swift/obj/ssync_sender.py +113 -54
- swift/obj/updater.py +652 -139
- swift/obj/watchers/__init__.py +0 -0
- swift/obj/watchers/dark_data.py +213 -0
- swift/proxy/controllers/account.py +11 -11
- swift/proxy/controllers/base.py +848 -604
- swift/proxy/controllers/container.py +433 -92
- swift/proxy/controllers/info.py +3 -2
- swift/proxy/controllers/obj.py +1000 -489
- swift/proxy/server.py +185 -112
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/AUTHORS +58 -11
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/METADATA +51 -56
- swift-2.35.0.dist-info/RECORD +201 -0
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/WHEEL +1 -1
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/entry_points.txt +43 -0
- swift-2.35.0.dist-info/pbr.json +1 -0
- swift/locale/de/LC_MESSAGES/swift.po +0 -1216
- swift/locale/en_GB/LC_MESSAGES/swift.po +0 -1207
- swift/locale/es/LC_MESSAGES/swift.po +0 -1085
- swift/locale/fr/LC_MESSAGES/swift.po +0 -909
- swift/locale/it/LC_MESSAGES/swift.po +0 -894
- swift/locale/ja/LC_MESSAGES/swift.po +0 -965
- swift/locale/ko_KR/LC_MESSAGES/swift.po +0 -964
- swift/locale/pt_BR/LC_MESSAGES/swift.po +0 -881
- swift/locale/ru/LC_MESSAGES/swift.po +0 -891
- swift/locale/tr_TR/LC_MESSAGES/swift.po +0 -832
- swift/locale/zh_CN/LC_MESSAGES/swift.po +0 -833
- swift/locale/zh_TW/LC_MESSAGES/swift.po +0 -838
- swift-2.23.3.data/scripts/swift-account-auditor +0 -23
- swift-2.23.3.data/scripts/swift-account-info +0 -51
- swift-2.23.3.data/scripts/swift-account-reaper +0 -23
- swift-2.23.3.data/scripts/swift-account-replicator +0 -34
- swift-2.23.3.data/scripts/swift-account-server +0 -23
- swift-2.23.3.data/scripts/swift-container-auditor +0 -23
- swift-2.23.3.data/scripts/swift-container-info +0 -55
- swift-2.23.3.data/scripts/swift-container-reconciler +0 -21
- swift-2.23.3.data/scripts/swift-container-replicator +0 -34
- swift-2.23.3.data/scripts/swift-container-sharder +0 -37
- swift-2.23.3.data/scripts/swift-container-sync +0 -23
- swift-2.23.3.data/scripts/swift-container-updater +0 -23
- swift-2.23.3.data/scripts/swift-dispersion-report +0 -24
- swift-2.23.3.data/scripts/swift-form-signature +0 -20
- swift-2.23.3.data/scripts/swift-init +0 -119
- swift-2.23.3.data/scripts/swift-object-auditor +0 -29
- swift-2.23.3.data/scripts/swift-object-expirer +0 -33
- swift-2.23.3.data/scripts/swift-object-info +0 -60
- swift-2.23.3.data/scripts/swift-object-reconstructor +0 -33
- swift-2.23.3.data/scripts/swift-object-relinker +0 -41
- swift-2.23.3.data/scripts/swift-object-replicator +0 -37
- swift-2.23.3.data/scripts/swift-object-server +0 -27
- swift-2.23.3.data/scripts/swift-object-updater +0 -23
- swift-2.23.3.data/scripts/swift-proxy-server +0 -23
- swift-2.23.3.data/scripts/swift-recon +0 -24
- swift-2.23.3.data/scripts/swift-ring-builder +0 -24
- swift-2.23.3.data/scripts/swift-ring-builder-analyzer +0 -22
- swift-2.23.3.data/scripts/swift-ring-composer +0 -22
- swift-2.23.3.dist-info/RECORD +0 -220
- swift-2.23.3.dist-info/pbr.json +0 -1
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/LICENSE +0 -0
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/top_level.txt +0 -0
swift/obj/ssync_sender.py
CHANGED
@@ -13,20 +13,24 @@
|
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
15
|
|
16
|
-
import
|
17
|
-
|
16
|
+
from eventlet import sleep
|
17
|
+
import urllib
|
18
18
|
|
19
19
|
from swift.common import bufferedhttp
|
20
20
|
from swift.common import exceptions
|
21
21
|
from swift.common import http
|
22
|
+
from swift.common import utils
|
23
|
+
from swift.common.swob import wsgi_to_bytes
|
22
24
|
|
23
25
|
|
24
|
-
def encode_missing(object_hash, ts_data, ts_meta=None, ts_ctype=None
|
26
|
+
def encode_missing(object_hash, ts_data, ts_meta=None, ts_ctype=None,
|
27
|
+
**kwargs):
|
25
28
|
"""
|
26
|
-
Returns a string representing the object hash, its data file timestamp
|
27
|
-
|
28
|
-
non-zero, in the form:
|
29
|
-
``<hash> <ts_data> [m:<hex delta to ts_meta>[,t:<hex delta to ts_ctype>]
|
29
|
+
Returns a string representing the object hash, its data file timestamp,
|
30
|
+
the delta forwards to its metafile and content-type timestamps, if
|
31
|
+
non-zero, and its durability, in the form:
|
32
|
+
``<hash> <ts_data> [m:<hex delta to ts_meta>[,t:<hex delta to ts_ctype>]
|
33
|
+
[,durable:False]``
|
30
34
|
|
31
35
|
The decoder for this line is
|
32
36
|
:py:func:`~swift.obj.ssync_receiver.decode_missing`
|
@@ -34,12 +38,22 @@ def encode_missing(object_hash, ts_data, ts_meta=None, ts_ctype=None):
|
|
34
38
|
msg = ('%s %s'
|
35
39
|
% (urllib.parse.quote(object_hash),
|
36
40
|
urllib.parse.quote(ts_data.internal)))
|
41
|
+
extra_parts = []
|
37
42
|
if ts_meta and ts_meta != ts_data:
|
38
43
|
delta = ts_meta.raw - ts_data.raw
|
39
|
-
|
44
|
+
extra_parts.append('m:%x' % delta)
|
45
|
+
if ts_meta.offset:
|
46
|
+
extra_parts[-1] += '__%x' % ts_meta.offset
|
40
47
|
if ts_ctype and ts_ctype != ts_data:
|
41
48
|
delta = ts_ctype.raw - ts_data.raw
|
42
|
-
|
49
|
+
extra_parts.append('t:%x' % delta)
|
50
|
+
if ts_ctype.offset:
|
51
|
+
extra_parts[-1] += '__%x' % ts_ctype.offset
|
52
|
+
if 'durable' in kwargs and kwargs['durable'] is False:
|
53
|
+
# only send durable in the less common case that it is False
|
54
|
+
extra_parts.append('durable:%s' % kwargs['durable'])
|
55
|
+
if extra_parts:
|
56
|
+
msg = '%s %s' % (msg, ','.join(extra_parts))
|
43
57
|
return msg.encode('ascii')
|
44
58
|
|
45
59
|
|
@@ -133,7 +147,8 @@ class Sender(object):
|
|
133
147
|
process is there.
|
134
148
|
"""
|
135
149
|
|
136
|
-
def __init__(self, daemon, node, job, suffixes, remote_check_objs=None
|
150
|
+
def __init__(self, daemon, node, job, suffixes, remote_check_objs=None,
|
151
|
+
include_non_durable=False, max_objects=0):
|
137
152
|
self.daemon = daemon
|
138
153
|
self.df_mgr = self.daemon._df_router[job['policy']]
|
139
154
|
self.node = node
|
@@ -142,6 +157,9 @@ class Sender(object):
|
|
142
157
|
# When remote_check_objs is given in job, ssync_sender trys only to
|
143
158
|
# make sure those objects exist or not in remote.
|
144
159
|
self.remote_check_objs = remote_check_objs
|
160
|
+
self.include_non_durable = include_non_durable
|
161
|
+
self.max_objects = max_objects
|
162
|
+
self.limited_by_max_objects = False
|
145
163
|
|
146
164
|
def __call__(self):
|
147
165
|
"""
|
@@ -185,23 +203,27 @@ class Sender(object):
|
|
185
203
|
set(send_map.keys()))
|
186
204
|
can_delete_obj = dict((hash_, available_map[hash_])
|
187
205
|
for hash_ in in_sync_hashes)
|
206
|
+
self.daemon.logger.debug(
|
207
|
+
'ssync completed ok: dev: %s, part: %s, policy: %d, '
|
208
|
+
'num suffixes: %s, available: %d, sent: %d, deletable: %d',
|
209
|
+
self.job['device'], self.job['partition'],
|
210
|
+
self.job['policy'].idx, len(self.suffixes),
|
211
|
+
len(available_map), len(send_map), len(can_delete_obj))
|
188
212
|
return True, can_delete_obj
|
189
213
|
except (exceptions.MessageTimeout,
|
190
214
|
exceptions.ReplicationException) as err:
|
191
|
-
self.
|
192
|
-
|
193
|
-
|
194
|
-
self.job.get('partition'), err)
|
215
|
+
node_str = utils.node_to_string(self.node, replication=True)
|
216
|
+
self.daemon.logger.error('%s/%s %s', node_str,
|
217
|
+
self.job['partition'], err)
|
195
218
|
except Exception:
|
196
219
|
# We don't want any exceptions to escape our code and possibly
|
197
220
|
# mess up the original replicator code that called us since it
|
198
221
|
# was originally written to shell out to rsync which would do
|
199
222
|
# no such thing.
|
223
|
+
node_str = utils.node_to_string(self.node, replication=True)
|
200
224
|
self.daemon.logger.exception(
|
201
|
-
'%s
|
202
|
-
self.
|
203
|
-
self.node.get('replication_port'),
|
204
|
-
self.node.get('device'), self.job.get('partition'))
|
225
|
+
'%s/%s EXCEPTION in ssync.Sender',
|
226
|
+
node_str, self.job['partition'])
|
205
227
|
finally:
|
206
228
|
self.disconnect(connection)
|
207
229
|
except Exception:
|
@@ -221,11 +243,11 @@ class Sender(object):
|
|
221
243
|
with the object server.
|
222
244
|
"""
|
223
245
|
connection = response = None
|
246
|
+
node_addr = '%s:%s' % (self.node['replication_ip'],
|
247
|
+
self.node['replication_port'])
|
224
248
|
with exceptions.MessageTimeout(
|
225
249
|
self.daemon.conn_timeout, 'connect send'):
|
226
|
-
connection = SsyncBufferedHTTPConnection(
|
227
|
-
'%s:%s' % (self.node['replication_ip'],
|
228
|
-
self.node['replication_port']))
|
250
|
+
connection = SsyncBufferedHTTPConnection(node_addr)
|
229
251
|
connection.putrequest('SSYNC', '/%s/%s' % (
|
230
252
|
self.node['device'], self.job['partition']))
|
231
253
|
connection.putheader('Transfer-Encoding', 'chunked')
|
@@ -244,10 +266,18 @@ class Sender(object):
|
|
244
266
|
self.daemon.node_timeout, 'connect receive'):
|
245
267
|
response = connection.getresponse()
|
246
268
|
if response.status != http.HTTP_OK:
|
247
|
-
err_msg = response.read()
|
269
|
+
err_msg = utils.cap_length(response.read(), 1024)
|
248
270
|
raise exceptions.ReplicationException(
|
249
271
|
'Expected status %s; got %s (%s)' %
|
250
272
|
(http.HTTP_OK, response.status, err_msg))
|
273
|
+
if self.include_non_durable and not utils.config_true_value(
|
274
|
+
response.getheader('x-backend-accept-no-commit', False)):
|
275
|
+
# fall back to legacy behaviour if receiver does not understand
|
276
|
+
# X-Backend-Commit
|
277
|
+
self.daemon.logger.warning(
|
278
|
+
'ssync receiver %s does not accept non-durable fragments' %
|
279
|
+
node_addr)
|
280
|
+
self.include_non_durable = False
|
251
281
|
return connection, response
|
252
282
|
|
253
283
|
def missing_check(self, connection, response):
|
@@ -258,6 +288,7 @@ class Sender(object):
|
|
258
288
|
Full documentation of this can be found at
|
259
289
|
:py:meth:`.Receiver.missing_check`.
|
260
290
|
"""
|
291
|
+
self.limited_by_max_objects = False
|
261
292
|
available_map = {}
|
262
293
|
send_map = {}
|
263
294
|
# First, send our list.
|
@@ -265,22 +296,47 @@ class Sender(object):
|
|
265
296
|
self.daemon.node_timeout, 'missing_check start'):
|
266
297
|
msg = b':MISSING_CHECK: START\r\n'
|
267
298
|
connection.send(b'%x\r\n%s\r\n' % (len(msg), msg))
|
299
|
+
# an empty frag_prefs list is sufficient to get non-durable frags
|
300
|
+
# yielded, in which case an older durable frag will not be yielded
|
301
|
+
frag_prefs = [] if self.include_non_durable else None
|
268
302
|
hash_gen = self.df_mgr.yield_hashes(
|
269
303
|
self.job['device'], self.job['partition'],
|
270
304
|
self.job['policy'], self.suffixes,
|
271
|
-
frag_index=self.job.get('frag_index')
|
305
|
+
frag_index=self.job.get('frag_index'),
|
306
|
+
frag_prefs=frag_prefs)
|
272
307
|
if self.remote_check_objs is not None:
|
273
|
-
hash_gen =
|
308
|
+
hash_gen = filter(
|
274
309
|
lambda objhash_timestamps:
|
275
310
|
objhash_timestamps[0] in
|
276
311
|
self.remote_check_objs, hash_gen)
|
312
|
+
nlines = 0
|
313
|
+
nbytes = 0
|
314
|
+
object_hash = None
|
277
315
|
for object_hash, timestamps in hash_gen:
|
278
316
|
available_map[object_hash] = timestamps
|
279
317
|
with exceptions.MessageTimeout(
|
280
318
|
self.daemon.node_timeout,
|
281
|
-
'missing_check send line'
|
319
|
+
'missing_check send line: %d lines (%d bytes) sent'
|
320
|
+
% (nlines, nbytes)):
|
282
321
|
msg = b'%s\r\n' % encode_missing(object_hash, **timestamps)
|
283
|
-
|
322
|
+
msg = b'%x\r\n%s\r\n' % (len(msg), msg)
|
323
|
+
connection.send(msg)
|
324
|
+
if nlines % 5 == 0:
|
325
|
+
sleep() # Gives a chance for other greenthreads to run
|
326
|
+
nlines += 1
|
327
|
+
nbytes += len(msg)
|
328
|
+
if 0 < self.max_objects <= nlines:
|
329
|
+
break
|
330
|
+
for _ in hash_gen:
|
331
|
+
# only log truncation if there were more hashes to come...
|
332
|
+
self.limited_by_max_objects = True
|
333
|
+
self.daemon.logger.info(
|
334
|
+
'ssync missing_check truncated after %d objects: '
|
335
|
+
'device: %s, part: %s, policy: %s, last object hash: '
|
336
|
+
'%s', nlines, self.job['device'],
|
337
|
+
self.job['partition'], int(self.job['policy']),
|
338
|
+
object_hash)
|
339
|
+
break
|
284
340
|
with exceptions.MessageTimeout(
|
285
341
|
self.daemon.node_timeout, 'missing_check end'):
|
286
342
|
msg = b':MISSING_CHECK: END\r\n'
|
@@ -296,13 +352,12 @@ class Sender(object):
|
|
296
352
|
if line == b':MISSING_CHECK: START':
|
297
353
|
break
|
298
354
|
elif line:
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
pass
|
355
|
+
try:
|
356
|
+
line = line.decode('ascii')
|
357
|
+
except UnicodeDecodeError:
|
358
|
+
pass
|
304
359
|
raise exceptions.ReplicationException(
|
305
|
-
'Unexpected response: %r' % line
|
360
|
+
'Unexpected response: %r' % utils.cap_length(line, 1024))
|
306
361
|
while True:
|
307
362
|
with exceptions.MessageTimeout(
|
308
363
|
self.daemon.http_timeout, 'missing_check line wait'):
|
@@ -330,13 +385,15 @@ class Sender(object):
|
|
330
385
|
self.daemon.node_timeout, 'updates start'):
|
331
386
|
msg = b':UPDATES: START\r\n'
|
332
387
|
connection.send(b'%x\r\n%s\r\n' % (len(msg), msg))
|
388
|
+
frag_prefs = [] if self.include_non_durable else None
|
389
|
+
updates = 0
|
333
390
|
for object_hash, want in send_map.items():
|
334
391
|
object_hash = urllib.parse.unquote(object_hash)
|
335
392
|
try:
|
336
393
|
df = self.df_mgr.get_diskfile_from_hash(
|
337
394
|
self.job['device'], self.job['partition'], object_hash,
|
338
395
|
self.job['policy'], frag_index=self.job.get('frag_index'),
|
339
|
-
open_expired=True)
|
396
|
+
open_expired=True, frag_prefs=frag_prefs)
|
340
397
|
except exceptions.DiskFileNotExist:
|
341
398
|
continue
|
342
399
|
url_path = urllib.parse.quote(
|
@@ -344,13 +401,15 @@ class Sender(object):
|
|
344
401
|
try:
|
345
402
|
df.open()
|
346
403
|
if want.get('data'):
|
404
|
+
is_durable = (df.durable_timestamp == df.data_timestamp)
|
347
405
|
# EC reconstructor may have passed a callback to build an
|
348
406
|
# alternative diskfile - construct it using the metadata
|
349
407
|
# from the data file only.
|
350
408
|
df_alt = self.job.get(
|
351
409
|
'sync_diskfile_builder', lambda *args: df)(
|
352
|
-
self.job, self.node, df
|
353
|
-
self.send_put(connection, url_path, df_alt
|
410
|
+
self.job, self.node, df)
|
411
|
+
self.send_put(connection, url_path, df_alt,
|
412
|
+
durable=is_durable)
|
354
413
|
if want.get('meta') and df.data_timestamp != df.timestamp:
|
355
414
|
self.send_post(connection, url_path, df)
|
356
415
|
except exceptions.DiskFileDeleted as err:
|
@@ -363,6 +422,9 @@ class Sender(object):
|
|
363
422
|
# continue. The diskfile may however be deleted after a
|
364
423
|
# successful ssync since it remains in the send_map.
|
365
424
|
pass
|
425
|
+
if updates % 5 == 0:
|
426
|
+
sleep() # Gives a chance for other greenthreads to run
|
427
|
+
updates += 1
|
366
428
|
with exceptions.MessageTimeout(
|
367
429
|
self.daemon.node_timeout, 'updates end'):
|
368
430
|
msg = b':UPDATES: END\r\n'
|
@@ -378,13 +440,12 @@ class Sender(object):
|
|
378
440
|
if line == b':UPDATES: START':
|
379
441
|
break
|
380
442
|
elif line:
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
pass
|
443
|
+
try:
|
444
|
+
line = line.decode('ascii')
|
445
|
+
except UnicodeDecodeError:
|
446
|
+
pass
|
386
447
|
raise exceptions.ReplicationException(
|
387
|
-
'Unexpected response: %r' % line
|
448
|
+
'Unexpected response: %r' % utils.cap_length(line, 1024))
|
388
449
|
while True:
|
389
450
|
with exceptions.MessageTimeout(
|
390
451
|
self.daemon.http_timeout, 'updates line wait'):
|
@@ -395,23 +456,17 @@ class Sender(object):
|
|
395
456
|
if line == b':UPDATES: END':
|
396
457
|
break
|
397
458
|
elif line:
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
pass
|
459
|
+
try:
|
460
|
+
line = line.decode('ascii')
|
461
|
+
except UnicodeDecodeError:
|
462
|
+
pass
|
403
463
|
raise exceptions.ReplicationException(
|
404
|
-
'Unexpected response: %r' % line
|
464
|
+
'Unexpected response: %r' % utils.cap_length(line, 1024))
|
405
465
|
|
406
466
|
def send_subrequest(self, connection, method, url_path, headers, df):
|
407
467
|
msg = [b'%s %s' % (method.encode('ascii'), url_path.encode('utf8'))]
|
408
468
|
for key, value in sorted(headers.items()):
|
409
|
-
|
410
|
-
msg.append(b'%s: %s' % (key, value))
|
411
|
-
else:
|
412
|
-
msg.append(b'%s: %s' % (
|
413
|
-
key.encode('utf8', 'surrogateescape'),
|
414
|
-
str(value).encode('utf8', 'surrogateescape')))
|
469
|
+
msg.append(wsgi_to_bytes('%s: %s' % (key, value)))
|
415
470
|
msg = b'\r\n'.join(msg) + b'\r\n\r\n'
|
416
471
|
with exceptions.MessageTimeout(self.daemon.node_timeout,
|
417
472
|
'send_%s' % method.lower()):
|
@@ -443,12 +498,16 @@ class Sender(object):
|
|
443
498
|
headers = {'X-Timestamp': timestamp.internal}
|
444
499
|
self.send_subrequest(connection, 'DELETE', url_path, headers, None)
|
445
500
|
|
446
|
-
def send_put(self, connection, url_path, df):
|
501
|
+
def send_put(self, connection, url_path, df, durable=True):
|
447
502
|
"""
|
448
503
|
Sends a PUT subrequest for the url_path using the source df
|
449
504
|
(DiskFile) and content_length.
|
450
505
|
"""
|
451
506
|
headers = {'Content-Length': str(df.content_length)}
|
507
|
+
if not durable:
|
508
|
+
# only send this header for the less common case; without this
|
509
|
+
# header object servers assume default commit behaviour
|
510
|
+
headers['X-Backend-No-Commit'] = 'True'
|
452
511
|
for key, value in df.get_datafile_metadata().items():
|
453
512
|
if key not in ('name', 'Content-Length'):
|
454
513
|
headers[key] = value
|