swift 2.23.2__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.2.data/scripts/swift-account-audit → swift/cli/account_audit.py +23 -13
- swift-2.23.2.data/scripts/swift-config → swift/cli/config.py +2 -2
- swift/cli/container_deleter.py +5 -11
- swift-2.23.2.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +8 -7
- swift/cli/dispersion_report.py +10 -9
- swift-2.23.2.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +63 -21
- swift/cli/form_signature.py +3 -7
- swift-2.23.2.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +8 -2
- swift/cli/info.py +183 -29
- swift/cli/manage_shard_ranges.py +708 -37
- swift-2.23.2.data/scripts/swift-oldies → swift/cli/oldies.py +25 -14
- swift-2.23.2.data/scripts/swift-orphans → swift/cli/orphans.py +7 -3
- swift/cli/recon.py +196 -67
- swift-2.23.2.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +17 -20
- swift-2.23.2.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 +198 -127
- 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 +396 -147
- 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 -81
- 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 +52 -19
- swift/common/middleware/tempauth.py +76 -58
- swift/common/middleware/tempurl.py +192 -174
- 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.2.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} +2191 -2762
- 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 +555 -536
- swift/container/auditor.py +14 -100
- swift/container/backend.py +552 -227
- swift/container/reconciler.py +126 -37
- swift/container/replicator.py +96 -22
- swift/container/server.py +397 -176
- swift/container/sharder.py +1580 -639
- 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 +213 -122
- swift/obj/ssync_receiver.py +145 -85
- swift/obj/ssync_sender.py +113 -54
- swift/obj/updater.py +653 -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 +452 -86
- swift/proxy/controllers/info.py +3 -2
- swift/proxy/controllers/obj.py +1009 -490
- swift/proxy/server.py +185 -112
- swift-2.35.0.dist-info/AUTHORS +501 -0
- swift-2.35.0.dist-info/LICENSE +202 -0
- {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/METADATA +52 -61
- swift-2.35.0.dist-info/RECORD +201 -0
- {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/WHEEL +1 -1
- {swift-2.23.2.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.2.data/scripts/swift-account-auditor +0 -23
- swift-2.23.2.data/scripts/swift-account-info +0 -51
- swift-2.23.2.data/scripts/swift-account-reaper +0 -23
- swift-2.23.2.data/scripts/swift-account-replicator +0 -34
- swift-2.23.2.data/scripts/swift-account-server +0 -23
- swift-2.23.2.data/scripts/swift-container-auditor +0 -23
- swift-2.23.2.data/scripts/swift-container-info +0 -51
- swift-2.23.2.data/scripts/swift-container-reconciler +0 -21
- swift-2.23.2.data/scripts/swift-container-replicator +0 -34
- swift-2.23.2.data/scripts/swift-container-sharder +0 -33
- swift-2.23.2.data/scripts/swift-container-sync +0 -23
- swift-2.23.2.data/scripts/swift-container-updater +0 -23
- swift-2.23.2.data/scripts/swift-dispersion-report +0 -24
- swift-2.23.2.data/scripts/swift-form-signature +0 -20
- swift-2.23.2.data/scripts/swift-init +0 -119
- swift-2.23.2.data/scripts/swift-object-auditor +0 -29
- swift-2.23.2.data/scripts/swift-object-expirer +0 -33
- swift-2.23.2.data/scripts/swift-object-info +0 -60
- swift-2.23.2.data/scripts/swift-object-reconstructor +0 -33
- swift-2.23.2.data/scripts/swift-object-relinker +0 -41
- swift-2.23.2.data/scripts/swift-object-replicator +0 -37
- swift-2.23.2.data/scripts/swift-object-server +0 -27
- swift-2.23.2.data/scripts/swift-object-updater +0 -23
- swift-2.23.2.data/scripts/swift-proxy-server +0 -23
- swift-2.23.2.data/scripts/swift-recon +0 -24
- swift-2.23.2.data/scripts/swift-ring-builder +0 -24
- swift-2.23.2.data/scripts/swift-ring-builder-analyzer +0 -22
- swift-2.23.2.data/scripts/swift-ring-composer +0 -22
- swift-2.23.2.dist-info/DESCRIPTION.rst +0 -166
- swift-2.23.2.dist-info/RECORD +0 -220
- swift-2.23.2.dist-info/metadata.json +0 -1
- swift-2.23.2.dist-info/pbr.json +0 -1
- {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/top_level.txt +0 -0
swift/obj/ssync_receiver.py
CHANGED
@@ -16,7 +16,8 @@
|
|
16
16
|
|
17
17
|
import eventlet.greenio
|
18
18
|
import eventlet.wsgi
|
19
|
-
from
|
19
|
+
from eventlet import sleep
|
20
|
+
import urllib
|
20
21
|
|
21
22
|
from swift.common import exceptions
|
22
23
|
from swift.common import http
|
@@ -26,11 +27,16 @@ from swift.common import request_helpers
|
|
26
27
|
from swift.common.utils import Timestamp
|
27
28
|
|
28
29
|
|
30
|
+
class SsyncClientDisconnected(Exception):
|
31
|
+
pass
|
32
|
+
|
33
|
+
|
29
34
|
def decode_missing(line):
|
30
35
|
"""
|
31
36
|
Parse a string of the form generated by
|
32
37
|
:py:func:`~swift.obj.ssync_sender.encode_missing` and return a dict
|
33
|
-
with keys ``object_hash``, ``ts_data``, ``ts_meta``, ``ts_ctype
|
38
|
+
with keys ``object_hash``, ``ts_data``, ``ts_meta``, ``ts_ctype``,
|
39
|
+
``durable``.
|
34
40
|
|
35
41
|
The encoder for this line is
|
36
42
|
:py:func:`~swift.obj.ssync_sender.encode_missing`
|
@@ -39,17 +45,28 @@ def decode_missing(line):
|
|
39
45
|
parts = line.decode('ascii').split()
|
40
46
|
result['object_hash'] = urllib.parse.unquote(parts[0])
|
41
47
|
t_data = urllib.parse.unquote(parts[1])
|
42
|
-
result['ts_data'] = Timestamp(t_data)
|
43
|
-
result['ts_meta'] = result['ts_ctype'] =
|
48
|
+
result['ts_data'] = ts_data = Timestamp(t_data)
|
49
|
+
result['ts_meta'] = result['ts_ctype'] = ts_data
|
50
|
+
result['durable'] = True # default to True in case this key isn't sent
|
44
51
|
if len(parts) > 2:
|
45
52
|
# allow for a comma separated list of k:v pairs to future-proof
|
46
53
|
subparts = urllib.parse.unquote(parts[2]).split(',')
|
47
54
|
for item in [subpart for subpart in subparts if ':' in subpart]:
|
48
55
|
k, v = item.split(':')
|
49
56
|
if k == 'm':
|
50
|
-
|
57
|
+
v, _, o = v.partition('__')
|
58
|
+
# ignore ts_data offset when calculating ts_meta
|
59
|
+
result['ts_meta'] = Timestamp(ts_data.normal,
|
60
|
+
delta=int(v, 16),
|
61
|
+
offset=int(o or '0', 16))
|
51
62
|
elif k == 't':
|
52
|
-
|
63
|
+
v, _, o = v.partition('__')
|
64
|
+
# ignore ts_data offset when calculating ts_ctype
|
65
|
+
result['ts_ctype'] = Timestamp(Timestamp(ts_data).normal,
|
66
|
+
delta=int(v, 16),
|
67
|
+
offset=int(o or '0', 16))
|
68
|
+
elif k == 'durable':
|
69
|
+
result['durable'] = utils.config_true_value(v)
|
53
70
|
return result
|
54
71
|
|
55
72
|
|
@@ -107,17 +124,17 @@ class Receiver(object):
|
|
107
124
|
|
108
125
|
The general process inside an SSYNC request is:
|
109
126
|
|
110
|
-
|
111
|
-
|
127
|
+
1. Initialize the request: Basic request validation, mount check,
|
128
|
+
acquire semaphore lock, etc..
|
112
129
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
130
|
+
2. Missing check: Sender sends the hashes and timestamps of
|
131
|
+
the object information it can send, receiver sends back
|
132
|
+
the hashes it wants (doesn't have or has an older
|
133
|
+
timestamp).
|
117
134
|
|
118
|
-
|
135
|
+
3. Updates: Sender sends the object information requested.
|
119
136
|
|
120
|
-
|
137
|
+
4. Close down: Release semaphore lock, etc.
|
121
138
|
"""
|
122
139
|
|
123
140
|
def __init__(self, app, request):
|
@@ -141,6 +158,7 @@ class Receiver(object):
|
|
141
158
|
"""
|
142
159
|
# The general theme for functions __call__ calls is that they should
|
143
160
|
# raise exceptions.MessageTimeout for client timeouts (logged locally),
|
161
|
+
# exceptions.ChunkReadError for client disconnects (logged locally),
|
144
162
|
# swob.HTTPException classes for exceptions to return to the caller but
|
145
163
|
# not log locally (unmounted, for example), and any other Exceptions
|
146
164
|
# will be logged with a full stack trace.
|
@@ -172,7 +190,10 @@ class Receiver(object):
|
|
172
190
|
finally:
|
173
191
|
if self.app.replication_semaphore:
|
174
192
|
self.app.replication_semaphore.release()
|
175
|
-
except
|
193
|
+
except SsyncClientDisconnected:
|
194
|
+
self.app.logger.error('ssync client disconnected')
|
195
|
+
self.disconnect = True
|
196
|
+
except exceptions.LockTimeout as err:
|
176
197
|
self.app.logger.debug(
|
177
198
|
'%s/%s/%s SSYNC LOCK TIMEOUT: %s' % (
|
178
199
|
self.request.remote_addr, self.device, self.partition,
|
@@ -184,6 +205,11 @@ class Receiver(object):
|
|
184
205
|
self.request.remote_addr, self.device, self.partition,
|
185
206
|
err))
|
186
207
|
yield (':ERROR: %d %r\n' % (408, str(err))).encode('utf8')
|
208
|
+
except exceptions.ChunkReadError as err:
|
209
|
+
self.app.logger.error(
|
210
|
+
'%s/%s/%s read failed in ssync.Receiver: %s' % (
|
211
|
+
self.request.remote_addr, self.device, self.partition,
|
212
|
+
err))
|
187
213
|
except swob.HTTPException as err:
|
188
214
|
body = b''.join(err({}, lambda *args: None))
|
189
215
|
yield (':ERROR: %d %r\n' % (
|
@@ -236,6 +262,17 @@ class Receiver(object):
|
|
236
262
|
raise swob.HTTPInsufficientStorage(drive=self.device)
|
237
263
|
self.fp = self.request.environ['wsgi.input']
|
238
264
|
|
265
|
+
def _readline(self, context):
|
266
|
+
# try to read a line from the wsgi input; annotate any timeout or read
|
267
|
+
# errors with a description of the calling context
|
268
|
+
with exceptions.MessageTimeout(
|
269
|
+
self.app.client_timeout, context):
|
270
|
+
try:
|
271
|
+
line = self.fp.readline(self.app.network_chunk_size)
|
272
|
+
except (eventlet.wsgi.ChunkReadError, IOError) as err:
|
273
|
+
raise exceptions.ChunkReadError('%s: %s' % (context, err))
|
274
|
+
return line
|
275
|
+
|
239
276
|
def _check_local(self, remote, make_durable=True):
|
240
277
|
"""
|
241
278
|
Parse local diskfile and return results of current
|
@@ -254,6 +291,7 @@ class Receiver(object):
|
|
254
291
|
except exceptions.DiskFileDeleted as err:
|
255
292
|
result = {'ts_data': err.timestamp}
|
256
293
|
except exceptions.DiskFileError:
|
294
|
+
# e.g. a non-durable EC frag
|
257
295
|
result = {}
|
258
296
|
else:
|
259
297
|
result = {
|
@@ -261,25 +299,35 @@ class Receiver(object):
|
|
261
299
|
'ts_meta': df.timestamp,
|
262
300
|
'ts_ctype': df.content_type_timestamp,
|
263
301
|
}
|
264
|
-
if (
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
#
|
270
|
-
#
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
302
|
+
if ((df.durable_timestamp is None or
|
303
|
+
df.durable_timestamp < remote['ts_data']) and
|
304
|
+
df.fragments and
|
305
|
+
remote['ts_data'] in df.fragments and
|
306
|
+
self.frag_index in df.fragments[remote['ts_data']]):
|
307
|
+
# The remote is offering a fragment that we already have but is
|
308
|
+
# *newer* than anything *durable* that we have
|
309
|
+
if remote['durable']:
|
310
|
+
# We have the frag, just missing durable state, so make the
|
311
|
+
# frag durable now. Try this just once to avoid looping if
|
312
|
+
# it fails.
|
313
|
+
if make_durable:
|
314
|
+
try:
|
315
|
+
with df.create() as writer:
|
316
|
+
writer.commit(remote['ts_data'])
|
317
|
+
return self._check_local(remote, make_durable=False)
|
318
|
+
except Exception:
|
319
|
+
# if commit fails then log exception and fall back to
|
320
|
+
# wanting a full update
|
321
|
+
self.app.logger.exception(
|
322
|
+
'%s/%s/%s EXCEPTION in ssync.Receiver while '
|
323
|
+
'attempting commit of %s'
|
324
|
+
% (self.request.remote_addr, self.device,
|
325
|
+
self.partition, df._datadir))
|
326
|
+
else:
|
327
|
+
# We have the non-durable frag that is on offer, but our
|
328
|
+
# ts_data may currently be set to an older durable frag, so
|
329
|
+
# bump our ts_data to prevent the remote frag being wanted.
|
330
|
+
result['ts_data'] = remote['ts_data']
|
283
331
|
return result
|
284
332
|
|
285
333
|
def _check_missing(self, line):
|
@@ -306,50 +354,54 @@ class Receiver(object):
|
|
306
354
|
|
307
355
|
The process is generally:
|
308
356
|
|
309
|
-
|
310
|
-
|
357
|
+
1. Sender sends ``:MISSING_CHECK: START`` and begins
|
358
|
+
sending `hash timestamp` lines.
|
311
359
|
|
312
|
-
|
313
|
-
|
314
|
-
|
360
|
+
2. Receiver gets ``:MISSING_CHECK: START`` and begins
|
361
|
+
reading the `hash timestamp` lines, collecting the
|
362
|
+
hashes of those it desires.
|
315
363
|
|
316
|
-
|
364
|
+
3. Sender sends ``:MISSING_CHECK: END``.
|
317
365
|
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
366
|
+
4. Receiver gets ``:MISSING_CHECK: END``, responds with
|
367
|
+
``:MISSING_CHECK: START``, followed by the list of
|
368
|
+
<wanted_hash> specifiers it collected as being wanted
|
369
|
+
(one per line), ``:MISSING_CHECK: END``, and flushes any
|
370
|
+
buffers.
|
323
371
|
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
372
|
+
Each <wanted_hash> specifier has the form <hash>[ <parts>] where
|
373
|
+
<parts> is a string containing characters 'd' and/or 'm'
|
374
|
+
indicating that only data or meta part of object respectively is
|
375
|
+
required to be sync'd.
|
328
376
|
|
329
|
-
|
330
|
-
|
331
|
-
|
377
|
+
5. Sender gets ``:MISSING_CHECK: START`` and reads the list
|
378
|
+
of hashes desired by the receiver until reading
|
379
|
+
``:MISSING_CHECK: END``.
|
332
380
|
|
333
381
|
The collection and then response is so the sender doesn't
|
334
382
|
have to read while it writes to ensure network buffers don't
|
335
383
|
fill up and block everything.
|
336
384
|
"""
|
337
|
-
|
338
|
-
|
339
|
-
|
385
|
+
line = self._readline('missing_check start')
|
386
|
+
if not line:
|
387
|
+
# Guess they hung up
|
388
|
+
raise SsyncClientDisconnected
|
340
389
|
if line.strip() != b':MISSING_CHECK: START':
|
341
390
|
raise Exception(
|
342
|
-
'Looking for :MISSING_CHECK: START got %r'
|
391
|
+
'Looking for :MISSING_CHECK: START got %r'
|
392
|
+
% utils.cap_length(line, 1024))
|
343
393
|
object_hashes = []
|
394
|
+
nlines = 0
|
344
395
|
while True:
|
345
|
-
|
346
|
-
self.app.client_timeout, 'missing_check line'):
|
347
|
-
line = self.fp.readline(self.app.network_chunk_size)
|
396
|
+
line = self._readline('missing_check line')
|
348
397
|
if not line or line.strip() == b':MISSING_CHECK: END':
|
349
398
|
break
|
350
399
|
want = self._check_missing(line)
|
351
400
|
if want:
|
352
401
|
object_hashes.append(want)
|
402
|
+
if nlines % 5 == 0:
|
403
|
+
sleep() # Gives a chance for other greenthreads to run
|
404
|
+
nlines += 1
|
353
405
|
yield b':MISSING_CHECK: START\r\n'
|
354
406
|
if object_hashes:
|
355
407
|
yield b'\r\n'.join(hsh.encode('ascii') for hsh in object_hashes)
|
@@ -370,18 +422,18 @@ class Receiver(object):
|
|
370
422
|
|
371
423
|
The process is generally:
|
372
424
|
|
373
|
-
|
374
|
-
|
425
|
+
1. Sender sends ``:UPDATES: START`` and begins sending the
|
426
|
+
PUT and DELETE subrequests.
|
375
427
|
|
376
|
-
|
377
|
-
|
428
|
+
2. Receiver gets ``:UPDATES: START`` and begins routing the
|
429
|
+
subrequests to the object server.
|
378
430
|
|
379
|
-
|
431
|
+
3. Sender sends ``:UPDATES: END``.
|
380
432
|
|
381
|
-
|
382
|
-
|
433
|
+
4. Receiver gets ``:UPDATES: END`` and sends ``:UPDATES:
|
434
|
+
START`` and ``:UPDATES: END`` (assuming no errors).
|
383
435
|
|
384
|
-
|
436
|
+
5. Sender gets ``:UPDATES: START`` and ``:UPDATES: END``.
|
385
437
|
|
386
438
|
If too many subrequests fail, as configured by
|
387
439
|
replication_failure_threshold and replication_failure_ratio,
|
@@ -394,17 +446,18 @@ class Receiver(object):
|
|
394
446
|
success. This is so the sender knows if it can remove an out
|
395
447
|
of place partition, for example.
|
396
448
|
"""
|
397
|
-
|
398
|
-
|
399
|
-
|
449
|
+
line = self._readline('updates start')
|
450
|
+
if not line:
|
451
|
+
# Guess they hung up waiting for us to process the missing check
|
452
|
+
raise SsyncClientDisconnected
|
400
453
|
if line.strip() != b':UPDATES: START':
|
401
|
-
raise Exception('Looking for :UPDATES: START got %r'
|
454
|
+
raise Exception('Looking for :UPDATES: START got %r'
|
455
|
+
% utils.cap_length(line, 1024))
|
402
456
|
successes = 0
|
403
457
|
failures = 0
|
458
|
+
updates = 0
|
404
459
|
while True:
|
405
|
-
|
406
|
-
self.app.client_timeout, 'updates line'):
|
407
|
-
line = self.fp.readline(self.app.network_chunk_size)
|
460
|
+
line = self._readline('updates line')
|
408
461
|
if not line or line.strip() == b':UPDATES: END':
|
409
462
|
break
|
410
463
|
# Read first line METHOD PATH of subrequest.
|
@@ -416,22 +469,26 @@ class Receiver(object):
|
|
416
469
|
content_length = None
|
417
470
|
replication_headers = []
|
418
471
|
while True:
|
419
|
-
|
420
|
-
line = self.fp.readline(self.app.network_chunk_size)
|
472
|
+
line = self._readline('updates line')
|
421
473
|
if not line:
|
422
474
|
raise Exception(
|
423
475
|
'Got no headers for %s %s' % (method, path))
|
424
476
|
line = line.strip()
|
425
477
|
if not line:
|
426
478
|
break
|
427
|
-
header, value =
|
428
|
-
header = header.strip().lower()
|
429
|
-
value = value.strip()
|
479
|
+
header, value = line.split(b':', 1)
|
480
|
+
header = swob.bytes_to_wsgi(header.strip().lower())
|
481
|
+
value = swob.bytes_to_wsgi(value.strip())
|
430
482
|
subreq.headers[header] = value
|
431
|
-
if header
|
432
|
-
#
|
433
|
-
#
|
434
|
-
#
|
483
|
+
if header not in ('etag', 'x-backend-no-commit'):
|
484
|
+
# we'll use X-Backend-Replication-Headers to force the
|
485
|
+
# object server to write all sync'd metadata, but with some
|
486
|
+
# exceptions:
|
487
|
+
# - make sure ssync doesn't cause 'Etag' to be added to
|
488
|
+
# obj metadata in addition to 'ETag' which object server
|
489
|
+
# sets (note capitalization)
|
490
|
+
# - filter out x-backend-no-commit which ssync sender may
|
491
|
+
# have added to the subrequest
|
435
492
|
replication_headers.append(header)
|
436
493
|
if header == 'content-length':
|
437
494
|
content_length = int(value)
|
@@ -478,8 +535,8 @@ class Receiver(object):
|
|
478
535
|
successes += 1
|
479
536
|
else:
|
480
537
|
self.app.logger.warning(
|
481
|
-
'ssync subrequest failed with %s: %s %s' %
|
482
|
-
(resp.status_int, method, subreq.path))
|
538
|
+
'ssync subrequest failed with %s: %s %s (%s)' %
|
539
|
+
(resp.status_int, method, subreq.path, resp.body))
|
483
540
|
failures += 1
|
484
541
|
if failures >= self.app.replication_failure_threshold and (
|
485
542
|
not successes or
|
@@ -493,6 +550,9 @@ class Receiver(object):
|
|
493
550
|
# subreq.
|
494
551
|
for junk in subreq.environ['wsgi.input']:
|
495
552
|
pass
|
553
|
+
if updates % 5 == 0:
|
554
|
+
sleep() # Gives a chance for other greenthreads to run
|
555
|
+
updates += 1
|
496
556
|
if failures:
|
497
557
|
raise swob.HTTPInternalServerError(
|
498
558
|
'ERROR: With :UPDATES: %d failures to %d successes' %
|