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.
Files changed (206) hide show
  1. swift/__init__.py +29 -50
  2. swift/account/auditor.py +21 -118
  3. swift/account/backend.py +33 -28
  4. swift/account/reaper.py +37 -28
  5. swift/account/replicator.py +22 -0
  6. swift/account/server.py +60 -26
  7. swift/account/utils.py +28 -11
  8. swift-2.23.3.data/scripts/swift-account-audit → swift/cli/account_audit.py +23 -13
  9. swift-2.23.3.data/scripts/swift-config → swift/cli/config.py +2 -2
  10. swift/cli/container_deleter.py +5 -11
  11. swift-2.23.3.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +8 -7
  12. swift/cli/dispersion_report.py +10 -9
  13. swift-2.23.3.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +63 -21
  14. swift/cli/form_signature.py +3 -7
  15. swift-2.23.3.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +8 -2
  16. swift/cli/info.py +154 -14
  17. swift/cli/manage_shard_ranges.py +705 -37
  18. swift-2.23.3.data/scripts/swift-oldies → swift/cli/oldies.py +25 -14
  19. swift-2.23.3.data/scripts/swift-orphans → swift/cli/orphans.py +7 -3
  20. swift/cli/recon.py +196 -67
  21. swift-2.23.3.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +17 -20
  22. swift-2.23.3.data/scripts/swift-reconciler-enqueue → swift/cli/reconciler_enqueue.py +2 -3
  23. swift/cli/relinker.py +807 -126
  24. swift/cli/reload.py +135 -0
  25. swift/cli/ringbuilder.py +217 -20
  26. swift/cli/ringcomposer.py +0 -1
  27. swift/cli/shard-info.py +4 -3
  28. swift/common/base_storage_server.py +9 -20
  29. swift/common/bufferedhttp.py +48 -74
  30. swift/common/constraints.py +20 -15
  31. swift/common/container_sync_realms.py +9 -11
  32. swift/common/daemon.py +25 -8
  33. swift/common/db.py +195 -128
  34. swift/common/db_auditor.py +168 -0
  35. swift/common/db_replicator.py +95 -55
  36. swift/common/digest.py +141 -0
  37. swift/common/direct_client.py +144 -33
  38. swift/common/error_limiter.py +93 -0
  39. swift/common/exceptions.py +25 -1
  40. swift/common/header_key_dict.py +2 -9
  41. swift/common/http_protocol.py +373 -0
  42. swift/common/internal_client.py +129 -59
  43. swift/common/linkat.py +3 -4
  44. swift/common/manager.py +284 -67
  45. swift/common/memcached.py +390 -145
  46. swift/common/middleware/__init__.py +4 -0
  47. swift/common/middleware/account_quotas.py +211 -46
  48. swift/common/middleware/acl.py +3 -8
  49. swift/common/middleware/backend_ratelimit.py +230 -0
  50. swift/common/middleware/bulk.py +22 -34
  51. swift/common/middleware/catch_errors.py +1 -3
  52. swift/common/middleware/cname_lookup.py +6 -11
  53. swift/common/middleware/container_quotas.py +1 -1
  54. swift/common/middleware/container_sync.py +39 -17
  55. swift/common/middleware/copy.py +12 -0
  56. swift/common/middleware/crossdomain.py +22 -9
  57. swift/common/middleware/crypto/__init__.py +2 -1
  58. swift/common/middleware/crypto/crypto_utils.py +11 -15
  59. swift/common/middleware/crypto/decrypter.py +28 -11
  60. swift/common/middleware/crypto/encrypter.py +12 -17
  61. swift/common/middleware/crypto/keymaster.py +8 -15
  62. swift/common/middleware/crypto/kms_keymaster.py +2 -1
  63. swift/common/middleware/dlo.py +15 -11
  64. swift/common/middleware/domain_remap.py +5 -4
  65. swift/common/middleware/etag_quoter.py +128 -0
  66. swift/common/middleware/formpost.py +73 -70
  67. swift/common/middleware/gatekeeper.py +8 -1
  68. swift/common/middleware/keystoneauth.py +33 -3
  69. swift/common/middleware/list_endpoints.py +4 -4
  70. swift/common/middleware/listing_formats.py +85 -49
  71. swift/common/middleware/memcache.py +4 -95
  72. swift/common/middleware/name_check.py +3 -2
  73. swift/common/middleware/proxy_logging.py +160 -92
  74. swift/common/middleware/ratelimit.py +17 -10
  75. swift/common/middleware/read_only.py +6 -4
  76. swift/common/middleware/recon.py +59 -22
  77. swift/common/middleware/s3api/acl_handlers.py +25 -3
  78. swift/common/middleware/s3api/acl_utils.py +6 -1
  79. swift/common/middleware/s3api/controllers/__init__.py +6 -0
  80. swift/common/middleware/s3api/controllers/acl.py +3 -2
  81. swift/common/middleware/s3api/controllers/bucket.py +242 -137
  82. swift/common/middleware/s3api/controllers/logging.py +2 -2
  83. swift/common/middleware/s3api/controllers/multi_delete.py +43 -20
  84. swift/common/middleware/s3api/controllers/multi_upload.py +219 -133
  85. swift/common/middleware/s3api/controllers/obj.py +112 -8
  86. swift/common/middleware/s3api/controllers/object_lock.py +44 -0
  87. swift/common/middleware/s3api/controllers/s3_acl.py +2 -2
  88. swift/common/middleware/s3api/controllers/tagging.py +57 -0
  89. swift/common/middleware/s3api/controllers/versioning.py +36 -7
  90. swift/common/middleware/s3api/etree.py +22 -9
  91. swift/common/middleware/s3api/exception.py +0 -4
  92. swift/common/middleware/s3api/s3api.py +113 -41
  93. swift/common/middleware/s3api/s3request.py +384 -218
  94. swift/common/middleware/s3api/s3response.py +126 -23
  95. swift/common/middleware/s3api/s3token.py +16 -17
  96. swift/common/middleware/s3api/schema/delete.rng +1 -1
  97. swift/common/middleware/s3api/subresource.py +7 -10
  98. swift/common/middleware/s3api/utils.py +27 -10
  99. swift/common/middleware/slo.py +665 -358
  100. swift/common/middleware/staticweb.py +64 -37
  101. swift/common/middleware/symlink.py +51 -18
  102. swift/common/middleware/tempauth.py +76 -58
  103. swift/common/middleware/tempurl.py +191 -173
  104. swift/common/middleware/versioned_writes/__init__.py +51 -0
  105. swift/common/middleware/{versioned_writes.py → versioned_writes/legacy.py} +27 -26
  106. swift/common/middleware/versioned_writes/object_versioning.py +1482 -0
  107. swift/common/middleware/x_profile/exceptions.py +1 -4
  108. swift/common/middleware/x_profile/html_viewer.py +18 -19
  109. swift/common/middleware/x_profile/profile_model.py +1 -2
  110. swift/common/middleware/xprofile.py +10 -10
  111. swift-2.23.3.data/scripts/swift-container-server → swift/common/recon.py +13 -8
  112. swift/common/registry.py +147 -0
  113. swift/common/request_helpers.py +324 -57
  114. swift/common/ring/builder.py +67 -25
  115. swift/common/ring/composite_builder.py +1 -1
  116. swift/common/ring/ring.py +177 -51
  117. swift/common/ring/utils.py +1 -1
  118. swift/common/splice.py +10 -6
  119. swift/common/statsd_client.py +205 -0
  120. swift/common/storage_policy.py +49 -44
  121. swift/common/swob.py +86 -102
  122. swift/common/{utils.py → utils/__init__.py} +2163 -2772
  123. swift/common/utils/base.py +131 -0
  124. swift/common/utils/config.py +433 -0
  125. swift/common/utils/ipaddrs.py +256 -0
  126. swift/common/utils/libc.py +345 -0
  127. swift/common/utils/logs.py +859 -0
  128. swift/common/utils/timestamp.py +412 -0
  129. swift/common/wsgi.py +553 -535
  130. swift/container/auditor.py +14 -100
  131. swift/container/backend.py +490 -231
  132. swift/container/reconciler.py +126 -37
  133. swift/container/replicator.py +96 -22
  134. swift/container/server.py +358 -165
  135. swift/container/sharder.py +1540 -684
  136. swift/container/sync.py +94 -88
  137. swift/container/updater.py +53 -32
  138. swift/obj/auditor.py +153 -35
  139. swift/obj/diskfile.py +466 -217
  140. swift/obj/expirer.py +406 -124
  141. swift/obj/mem_diskfile.py +7 -4
  142. swift/obj/mem_server.py +1 -0
  143. swift/obj/reconstructor.py +523 -262
  144. swift/obj/replicator.py +249 -188
  145. swift/obj/server.py +207 -122
  146. swift/obj/ssync_receiver.py +145 -85
  147. swift/obj/ssync_sender.py +113 -54
  148. swift/obj/updater.py +652 -139
  149. swift/obj/watchers/__init__.py +0 -0
  150. swift/obj/watchers/dark_data.py +213 -0
  151. swift/proxy/controllers/account.py +11 -11
  152. swift/proxy/controllers/base.py +848 -604
  153. swift/proxy/controllers/container.py +433 -92
  154. swift/proxy/controllers/info.py +3 -2
  155. swift/proxy/controllers/obj.py +1000 -489
  156. swift/proxy/server.py +185 -112
  157. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/AUTHORS +58 -11
  158. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/METADATA +51 -56
  159. swift-2.35.0.dist-info/RECORD +201 -0
  160. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/WHEEL +1 -1
  161. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/entry_points.txt +43 -0
  162. swift-2.35.0.dist-info/pbr.json +1 -0
  163. swift/locale/de/LC_MESSAGES/swift.po +0 -1216
  164. swift/locale/en_GB/LC_MESSAGES/swift.po +0 -1207
  165. swift/locale/es/LC_MESSAGES/swift.po +0 -1085
  166. swift/locale/fr/LC_MESSAGES/swift.po +0 -909
  167. swift/locale/it/LC_MESSAGES/swift.po +0 -894
  168. swift/locale/ja/LC_MESSAGES/swift.po +0 -965
  169. swift/locale/ko_KR/LC_MESSAGES/swift.po +0 -964
  170. swift/locale/pt_BR/LC_MESSAGES/swift.po +0 -881
  171. swift/locale/ru/LC_MESSAGES/swift.po +0 -891
  172. swift/locale/tr_TR/LC_MESSAGES/swift.po +0 -832
  173. swift/locale/zh_CN/LC_MESSAGES/swift.po +0 -833
  174. swift/locale/zh_TW/LC_MESSAGES/swift.po +0 -838
  175. swift-2.23.3.data/scripts/swift-account-auditor +0 -23
  176. swift-2.23.3.data/scripts/swift-account-info +0 -51
  177. swift-2.23.3.data/scripts/swift-account-reaper +0 -23
  178. swift-2.23.3.data/scripts/swift-account-replicator +0 -34
  179. swift-2.23.3.data/scripts/swift-account-server +0 -23
  180. swift-2.23.3.data/scripts/swift-container-auditor +0 -23
  181. swift-2.23.3.data/scripts/swift-container-info +0 -55
  182. swift-2.23.3.data/scripts/swift-container-reconciler +0 -21
  183. swift-2.23.3.data/scripts/swift-container-replicator +0 -34
  184. swift-2.23.3.data/scripts/swift-container-sharder +0 -37
  185. swift-2.23.3.data/scripts/swift-container-sync +0 -23
  186. swift-2.23.3.data/scripts/swift-container-updater +0 -23
  187. swift-2.23.3.data/scripts/swift-dispersion-report +0 -24
  188. swift-2.23.3.data/scripts/swift-form-signature +0 -20
  189. swift-2.23.3.data/scripts/swift-init +0 -119
  190. swift-2.23.3.data/scripts/swift-object-auditor +0 -29
  191. swift-2.23.3.data/scripts/swift-object-expirer +0 -33
  192. swift-2.23.3.data/scripts/swift-object-info +0 -60
  193. swift-2.23.3.data/scripts/swift-object-reconstructor +0 -33
  194. swift-2.23.3.data/scripts/swift-object-relinker +0 -41
  195. swift-2.23.3.data/scripts/swift-object-replicator +0 -37
  196. swift-2.23.3.data/scripts/swift-object-server +0 -27
  197. swift-2.23.3.data/scripts/swift-object-updater +0 -23
  198. swift-2.23.3.data/scripts/swift-proxy-server +0 -23
  199. swift-2.23.3.data/scripts/swift-recon +0 -24
  200. swift-2.23.3.data/scripts/swift-ring-builder +0 -24
  201. swift-2.23.3.data/scripts/swift-ring-builder-analyzer +0 -22
  202. swift-2.23.3.data/scripts/swift-ring-composer +0 -22
  203. swift-2.23.3.dist-info/RECORD +0 -220
  204. swift-2.23.3.dist-info/pbr.json +0 -1
  205. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/LICENSE +0 -0
  206. {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 six
17
- from six.moves import urllib
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
- and the delta forwards to its metafile and content-type timestamps, if
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
- msg = '%s m:%x' % (msg, delta)
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
- msg = '%s,t:%x' % (msg, delta)
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.daemon.logger.error(
192
- '%s:%s/%s/%s %s', self.node.get('replication_ip'),
193
- self.node.get('replication_port'), self.node.get('device'),
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:%s/%s/%s EXCEPTION in ssync.Sender',
202
- self.node.get('replication_ip'),
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()[:1024]
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 = six.moves.filter(
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
- connection.send(b'%x\r\n%s\r\n' % (len(msg), msg))
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
- if not six.PY2:
300
- try:
301
- line = line.decode('ascii')
302
- except UnicodeDecodeError:
303
- pass
355
+ try:
356
+ line = line.decode('ascii')
357
+ except UnicodeDecodeError:
358
+ pass
304
359
  raise exceptions.ReplicationException(
305
- 'Unexpected response: %r' % line[:1024])
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.get_datafile_metadata())
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
- if not six.PY2:
382
- try:
383
- line = line.decode('ascii')
384
- except UnicodeDecodeError:
385
- pass
443
+ try:
444
+ line = line.decode('ascii')
445
+ except UnicodeDecodeError:
446
+ pass
386
447
  raise exceptions.ReplicationException(
387
- 'Unexpected response: %r' % line[:1024])
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
- if not six.PY2:
399
- try:
400
- line = line.decode('ascii')
401
- except UnicodeDecodeError:
402
- pass
459
+ try:
460
+ line = line.decode('ascii')
461
+ except UnicodeDecodeError:
462
+ pass
403
463
  raise exceptions.ReplicationException(
404
- 'Unexpected response: %r' % line[:1024])
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
- if six.PY2:
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