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/common/memcached.py
CHANGED
@@ -44,19 +44,23 @@ version is at:
|
|
44
44
|
http://github.com/memcached/memcached/blob/1.4.2/doc/protocol.txt
|
45
45
|
"""
|
46
46
|
|
47
|
-
import
|
48
|
-
import six.moves.cPickle as pickle
|
47
|
+
import os
|
49
48
|
import json
|
50
49
|
import logging
|
51
|
-
|
50
|
+
# the name of 'time' module is changed to 'tm', to avoid changing the
|
51
|
+
# signatures of member functions in this file.
|
52
|
+
import time as tm
|
52
53
|
from bisect import bisect
|
53
|
-
from hashlib import md5
|
54
54
|
|
55
|
-
from eventlet.green import socket
|
55
|
+
from eventlet.green import socket, ssl
|
56
56
|
from eventlet.pools import Pool
|
57
57
|
from eventlet import Timeout
|
58
|
-
from
|
58
|
+
from configparser import ConfigParser, NoSectionError, NoOptionError
|
59
59
|
from swift.common import utils
|
60
|
+
from swift.common.exceptions import MemcacheConnectionError, \
|
61
|
+
MemcacheIncrNotFoundError, MemcachePoolTimeout
|
62
|
+
from swift.common.utils import md5, human_readable, config_true_value, \
|
63
|
+
memcached_timing_stats
|
60
64
|
|
61
65
|
DEFAULT_MEMCACHED_PORT = 11211
|
62
66
|
|
@@ -66,23 +70,27 @@ IO_TIMEOUT = 2.0
|
|
66
70
|
PICKLE_FLAG = 1
|
67
71
|
JSON_FLAG = 2
|
68
72
|
NODE_WEIGHT = 50
|
69
|
-
PICKLE_PROTOCOL = 2
|
70
73
|
TRY_COUNT = 3
|
71
74
|
|
72
75
|
# if ERROR_LIMIT_COUNT errors occur in ERROR_LIMIT_TIME seconds, the server
|
73
76
|
# will be considered failed for ERROR_LIMIT_DURATION seconds.
|
74
77
|
ERROR_LIMIT_COUNT = 10
|
75
|
-
ERROR_LIMIT_TIME = 60
|
76
|
-
|
78
|
+
ERROR_LIMIT_TIME = ERROR_LIMIT_DURATION = 60
|
79
|
+
DEFAULT_ITEM_SIZE_WARNING_THRESHOLD = -1
|
80
|
+
|
81
|
+
# Different sample rates for emitting Memcached timing stats.
|
82
|
+
TIMING_SAMPLE_RATE_HIGH = 0.1
|
83
|
+
TIMING_SAMPLE_RATE_MEDIUM = 0.01
|
84
|
+
TIMING_SAMPLE_RATE_LOW = 0.001
|
85
|
+
|
86
|
+
# The max value of a delta expiration time.
|
87
|
+
EXPTIME_MAXDELTA = 30 * 24 * 60 * 60
|
77
88
|
|
78
89
|
|
79
90
|
def md5hash(key):
|
80
91
|
if not isinstance(key, bytes):
|
81
|
-
|
82
|
-
|
83
|
-
else:
|
84
|
-
key = key.encode('utf-8', errors='surrogateescape')
|
85
|
-
return md5(key).hexdigest().encode('ascii')
|
92
|
+
key = key.encode('utf-8', errors='surrogateescape')
|
93
|
+
return md5(key, usedforsecurity=False).hexdigest().encode('ascii')
|
86
94
|
|
87
95
|
|
88
96
|
def sanitize_timeout(timeout):
|
@@ -92,8 +100,8 @@ def sanitize_timeout(timeout):
|
|
92
100
|
translates negative values to mean a delta of 30 days in seconds (and 1
|
93
101
|
additional second), client beware.
|
94
102
|
"""
|
95
|
-
if timeout >
|
96
|
-
timeout +=
|
103
|
+
if timeout > EXPTIME_MAXDELTA:
|
104
|
+
timeout += tm.time()
|
97
105
|
return int(timeout)
|
98
106
|
|
99
107
|
|
@@ -111,14 +119,6 @@ def set_msg(key, flags, timeout, value):
|
|
111
119
|
]) + (b'\r\n' + value + b'\r\n')
|
112
120
|
|
113
121
|
|
114
|
-
class MemcacheConnectionError(Exception):
|
115
|
-
pass
|
116
|
-
|
117
|
-
|
118
|
-
class MemcachePoolTimeout(Timeout):
|
119
|
-
pass
|
120
|
-
|
121
|
-
|
122
122
|
class MemcacheConnPool(Pool):
|
123
123
|
"""
|
124
124
|
Connection pool for Memcache Connections
|
@@ -128,11 +128,12 @@ class MemcacheConnPool(Pool):
|
|
128
128
|
:func:`swift.common.utils.parse_socket_string` for details.
|
129
129
|
"""
|
130
130
|
|
131
|
-
def __init__(self, server, size, connect_timeout):
|
131
|
+
def __init__(self, server, size, connect_timeout, tls_context=None):
|
132
132
|
Pool.__init__(self, max_size=size)
|
133
133
|
self.host, self.port = utils.parse_socket_string(
|
134
134
|
server, DEFAULT_MEMCACHED_PORT)
|
135
135
|
self._connect_timeout = connect_timeout
|
136
|
+
self._tls_context = tls_context
|
136
137
|
|
137
138
|
def create(self):
|
138
139
|
addrs = socket.getaddrinfo(self.host, self.port, socket.AF_UNSPEC,
|
@@ -140,16 +141,54 @@ class MemcacheConnPool(Pool):
|
|
140
141
|
family, socktype, proto, canonname, sockaddr = addrs[0]
|
141
142
|
sock = socket.socket(family, socket.SOCK_STREAM)
|
142
143
|
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
143
|
-
|
144
|
-
|
144
|
+
try:
|
145
|
+
with Timeout(self._connect_timeout):
|
146
|
+
sock.connect(sockaddr)
|
147
|
+
if self._tls_context:
|
148
|
+
sock = self._tls_context.wrap_socket(sock,
|
149
|
+
server_hostname=self.host)
|
150
|
+
except (Exception, Timeout):
|
151
|
+
sock.close()
|
152
|
+
raise
|
145
153
|
return (sock.makefile('rwb'), sock)
|
146
154
|
|
147
155
|
def get(self):
|
148
156
|
fp, sock = super(MemcacheConnPool, self).get()
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
157
|
+
try:
|
158
|
+
if fp is None:
|
159
|
+
# An error happened previously, so we need a new connection
|
160
|
+
fp, sock = self.create()
|
161
|
+
return fp, sock
|
162
|
+
except MemcachePoolTimeout:
|
163
|
+
# This is the only place that knows an item was successfully taken
|
164
|
+
# from the pool, so it has to be responsible for repopulating it.
|
165
|
+
# Any other errors should get handled in _get_conns(); see the
|
166
|
+
# comment about timeouts during create() there.
|
167
|
+
self.put((None, None))
|
168
|
+
raise
|
169
|
+
|
170
|
+
|
171
|
+
class MemcacheCommand(object):
|
172
|
+
"""
|
173
|
+
Helper class that encapsulates common parameters of a command.
|
174
|
+
|
175
|
+
:param method: the name of the MemcacheRing method that was called.
|
176
|
+
:param key: the memcached key.
|
177
|
+
"""
|
178
|
+
__slots__ = ('method', 'key', 'command', 'hash_key')
|
179
|
+
|
180
|
+
def __init__(self, method, key):
|
181
|
+
self.method = method
|
182
|
+
self.key = key
|
183
|
+
self.command = method.encode()
|
184
|
+
self.hash_key = md5hash(key)
|
185
|
+
|
186
|
+
@property
|
187
|
+
def key_prefix(self):
|
188
|
+
# get the prefix of a user provided memcache key by removing the
|
189
|
+
# content after the last '/', all current usages within swift are using
|
190
|
+
# prefix, such as "shard-updating-v2", "nvratelimit" and etc.
|
191
|
+
return self.key.rsplit('/', 1)[0]
|
153
192
|
|
154
193
|
|
155
194
|
class MemcacheRing(object):
|
@@ -157,39 +196,100 @@ class MemcacheRing(object):
|
|
157
196
|
Simple, consistent-hashed memcache client.
|
158
197
|
"""
|
159
198
|
|
160
|
-
def __init__(
|
161
|
-
|
162
|
-
|
163
|
-
|
199
|
+
def __init__(
|
200
|
+
self, servers, connect_timeout=CONN_TIMEOUT,
|
201
|
+
io_timeout=IO_TIMEOUT, pool_timeout=POOL_TIMEOUT,
|
202
|
+
tries=TRY_COUNT,
|
203
|
+
max_conns=2, tls_context=None, logger=None,
|
204
|
+
error_limit_count=ERROR_LIMIT_COUNT,
|
205
|
+
error_limit_time=ERROR_LIMIT_TIME,
|
206
|
+
error_limit_duration=ERROR_LIMIT_DURATION,
|
207
|
+
item_size_warning_threshold=DEFAULT_ITEM_SIZE_WARNING_THRESHOLD):
|
164
208
|
self._ring = {}
|
165
209
|
self._errors = dict(((serv, []) for serv in servers))
|
166
210
|
self._error_limited = dict(((serv, 0) for serv in servers))
|
211
|
+
self._error_limit_count = error_limit_count
|
212
|
+
self._error_limit_time = error_limit_time
|
213
|
+
self._error_limit_duration = error_limit_duration
|
167
214
|
for server in sorted(servers):
|
168
215
|
for i in range(NODE_WEIGHT):
|
169
216
|
self._ring[md5hash('%s-%s' % (server, i))] = server
|
170
217
|
self._tries = tries if tries <= len(servers) else len(servers)
|
171
218
|
self._sorted = sorted(self._ring)
|
172
|
-
self._client_cache = dict((
|
173
|
-
|
174
|
-
|
175
|
-
|
219
|
+
self._client_cache = dict((
|
220
|
+
(server, MemcacheConnPool(server, max_conns, connect_timeout,
|
221
|
+
tls_context=tls_context))
|
222
|
+
for server in servers))
|
176
223
|
self._connect_timeout = connect_timeout
|
177
224
|
self._io_timeout = io_timeout
|
178
225
|
self._pool_timeout = pool_timeout
|
179
|
-
|
180
|
-
|
226
|
+
if logger is None:
|
227
|
+
self.logger = logging.getLogger()
|
228
|
+
else:
|
229
|
+
self.logger = logger
|
230
|
+
self.item_size_warning_threshold = item_size_warning_threshold
|
181
231
|
|
182
|
-
|
183
|
-
|
232
|
+
@property
|
233
|
+
def memcache_servers(self):
|
234
|
+
return list(self._client_cache.keys())
|
235
|
+
|
236
|
+
def _log_error(self, server, cmd, action, msg):
|
237
|
+
self.logger.error(
|
238
|
+
"Error %(action)s to memcached: %(server)s"
|
239
|
+
": with key_prefix %(key_prefix)s, method %(method)s: %(msg)s",
|
240
|
+
{'action': action, 'server': server, 'key_prefix': cmd.key_prefix,
|
241
|
+
'method': cmd.method, 'msg': msg})
|
242
|
+
|
243
|
+
"""
|
244
|
+
Handles exceptions.
|
245
|
+
|
246
|
+
:param server: a server.
|
247
|
+
:param e: an exception.
|
248
|
+
:param cmd: an instance of MemcacheCommand.
|
249
|
+
:param conn_start_time: the time at which the failed operation started.
|
250
|
+
:param action: a verb describing the operation.
|
251
|
+
:param sock: an optional socket that needs to be closed by this method.
|
252
|
+
:param fp: an optional file pointer that needs to be closed by this method.
|
253
|
+
:param got_connection: if ``True``, the server's connection will be reset
|
254
|
+
in the cached connection pool.
|
255
|
+
"""
|
256
|
+
def _exception_occurred(self, server, e, cmd, conn_start_time,
|
257
|
+
action='talking', sock=None,
|
258
|
+
fp=None, got_connection=True):
|
184
259
|
if isinstance(e, Timeout):
|
185
|
-
|
186
|
-
|
260
|
+
self.logger.error(
|
261
|
+
"Timeout %(action)s to memcached: %(server)s"
|
262
|
+
": with key_prefix %(key_prefix)s, method %(method)s, "
|
263
|
+
"config_timeout %(config_timeout)s, time_spent %(time_spent)s",
|
264
|
+
{'action': action, 'server': server,
|
265
|
+
'key_prefix': cmd.key_prefix, 'method': cmd.method,
|
266
|
+
'config_timeout': e.seconds,
|
267
|
+
'time_spent': tm.time() - conn_start_time})
|
268
|
+
self.logger.timing_since(
|
269
|
+
'memcached.' + cmd.method + '.timeout.timing',
|
270
|
+
conn_start_time)
|
187
271
|
elif isinstance(e, (socket.error, MemcacheConnectionError)):
|
188
|
-
|
189
|
-
|
272
|
+
self.logger.error(
|
273
|
+
"Error %(action)s to memcached: %(server)s: "
|
274
|
+
"with key_prefix %(key_prefix)s, method %(method)s, "
|
275
|
+
"time_spent %(time_spent)s, %(err)s",
|
276
|
+
{'action': action, 'server': server,
|
277
|
+
'key_prefix': cmd.key_prefix, 'method': cmd.method,
|
278
|
+
'time_spent': tm.time() - conn_start_time, 'err': e})
|
279
|
+
self.logger.timing_since(
|
280
|
+
'memcached.' + cmd.method + '.conn_err.timing',
|
281
|
+
conn_start_time)
|
190
282
|
else:
|
191
|
-
|
192
|
-
|
283
|
+
self.logger.exception(
|
284
|
+
"Error %(action)s to memcached: %(server)s"
|
285
|
+
": with key_prefix %(key_prefix)s, method %(method)s, "
|
286
|
+
"time_spent %(time_spent)s",
|
287
|
+
{'action': action, 'server': server,
|
288
|
+
'key_prefix': cmd.key_prefix, 'method': cmd.method,
|
289
|
+
'time_spent': tm.time() - conn_start_time})
|
290
|
+
self.logger.timing_since(
|
291
|
+
'memcached.' + cmd.method + '.errors.timing', conn_start_time)
|
292
|
+
|
193
293
|
try:
|
194
294
|
if fp:
|
195
295
|
fp.close()
|
@@ -206,73 +306,93 @@ class MemcacheRing(object):
|
|
206
306
|
# We need to return something to the pool
|
207
307
|
# A new connection will be created the next time it is retrieved
|
208
308
|
self._return_conn(server, None, None)
|
209
|
-
|
210
|
-
|
211
|
-
|
309
|
+
|
310
|
+
if isinstance(e, MemcacheIncrNotFoundError):
|
311
|
+
# these errors can be caused by other greenthreads not yielding to
|
312
|
+
# the incr greenthread often enough, rather than a server problem,
|
313
|
+
# so don't error limit the server
|
314
|
+
return
|
315
|
+
|
316
|
+
if self._error_limit_time <= 0 or self._error_limit_duration <= 0:
|
317
|
+
return
|
318
|
+
|
319
|
+
now = tm.time()
|
320
|
+
self._errors[server].append(now)
|
321
|
+
if len(self._errors[server]) > self._error_limit_count:
|
212
322
|
self._errors[server] = [err for err in self._errors[server]
|
213
|
-
if err > now -
|
214
|
-
if len(self._errors[server]) >
|
215
|
-
self._error_limited[server] = now +
|
216
|
-
|
323
|
+
if err > now - self._error_limit_time]
|
324
|
+
if len(self._errors[server]) > self._error_limit_count:
|
325
|
+
self._error_limited[server] = now + self._error_limit_duration
|
326
|
+
self.logger.error('Error limiting server %s', server)
|
217
327
|
|
218
|
-
def _get_conns(self,
|
328
|
+
def _get_conns(self, cmd):
|
219
329
|
"""
|
220
330
|
Retrieves a server conn from the pool, or connects a new one.
|
221
331
|
Chooses the server based on a consistent hash of "key".
|
332
|
+
|
333
|
+
:param cmd: an instance of MemcacheCommand.
|
334
|
+
:return: generator to serve memcached connection
|
222
335
|
"""
|
223
|
-
pos = bisect(self._sorted,
|
336
|
+
pos = bisect(self._sorted, cmd.hash_key)
|
224
337
|
served = []
|
338
|
+
any_yielded = False
|
225
339
|
while len(served) < self._tries:
|
226
340
|
pos = (pos + 1) % len(self._sorted)
|
227
341
|
server = self._ring[self._sorted[pos]]
|
228
342
|
if server in served:
|
229
343
|
continue
|
230
344
|
served.append(server)
|
231
|
-
|
345
|
+
pool_start_time = tm.time()
|
346
|
+
if self._error_limited[server] > pool_start_time:
|
232
347
|
continue
|
233
348
|
sock = None
|
234
349
|
try:
|
235
350
|
with MemcachePoolTimeout(self._pool_timeout):
|
236
351
|
fp, sock = self._client_cache[server].get()
|
352
|
+
any_yielded = True
|
237
353
|
yield server, fp, sock
|
238
354
|
except MemcachePoolTimeout as e:
|
239
|
-
self._exception_occurred(
|
240
|
-
|
241
|
-
|
355
|
+
self._exception_occurred(server, e, cmd, pool_start_time,
|
356
|
+
action='getting a connection',
|
357
|
+
got_connection=False)
|
242
358
|
except (Exception, Timeout) as e:
|
243
359
|
# Typically a Timeout exception caught here is the one raised
|
244
360
|
# by the create() method of this server's MemcacheConnPool
|
245
361
|
# object.
|
246
|
-
self._exception_occurred(
|
247
|
-
|
362
|
+
self._exception_occurred(server, e, cmd, pool_start_time,
|
363
|
+
action='connecting', sock=sock)
|
364
|
+
if not any_yielded:
|
365
|
+
self._log_error('ALL', cmd, 'connecting',
|
366
|
+
'No more memcached servers to try')
|
248
367
|
|
249
368
|
def _return_conn(self, server, fp, sock):
|
250
369
|
"""Returns a server connection to the pool."""
|
251
370
|
self._client_cache[server].put((fp, sock))
|
252
371
|
|
372
|
+
# Sample rates of different memcached operations are based on generic
|
373
|
+
# swift usage patterns.
|
374
|
+
@memcached_timing_stats(sample_rate=TIMING_SAMPLE_RATE_HIGH)
|
253
375
|
def set(self, key, value, serialize=True, time=0,
|
254
|
-
min_compress_len=0):
|
376
|
+
min_compress_len=0, raise_on_error=False):
|
255
377
|
"""
|
256
378
|
Set a key/value pair in memcache
|
257
379
|
|
258
380
|
:param key: key
|
259
381
|
:param value: value
|
260
382
|
:param serialize: if True, value is serialized with JSON before sending
|
261
|
-
to memcache
|
262
|
-
pickle instead of JSON (to avoid cache poisoning)
|
383
|
+
to memcache
|
263
384
|
:param time: the time to live
|
264
385
|
:param min_compress_len: minimum compress length, this parameter was
|
265
386
|
added to keep the signature compatible with
|
266
387
|
python-memcached interface. This
|
267
388
|
implementation ignores it.
|
389
|
+
:param raise_on_error: if True, propagate Timeouts and other errors.
|
390
|
+
By default, errors are ignored.
|
268
391
|
"""
|
269
|
-
|
392
|
+
cmd = MemcacheCommand('set', key)
|
270
393
|
timeout = sanitize_timeout(time)
|
271
394
|
flags = 0
|
272
|
-
if serialize
|
273
|
-
value = pickle.dumps(value, PICKLE_PROTOCOL)
|
274
|
-
flags |= PICKLE_FLAG
|
275
|
-
elif serialize:
|
395
|
+
if serialize:
|
276
396
|
if isinstance(value, bytes):
|
277
397
|
value = value.decode('utf8')
|
278
398
|
value = json.dumps(value).encode('ascii')
|
@@ -280,55 +400,94 @@ class MemcacheRing(object):
|
|
280
400
|
elif not isinstance(value, bytes):
|
281
401
|
value = str(value).encode('utf-8')
|
282
402
|
|
283
|
-
|
403
|
+
if 0 <= self.item_size_warning_threshold <= len(value):
|
404
|
+
self.logger.warning(
|
405
|
+
"Item size larger than warning threshold: "
|
406
|
+
"%d (%s) >= %d (%s)", len(value),
|
407
|
+
human_readable(len(value)),
|
408
|
+
self.item_size_warning_threshold,
|
409
|
+
human_readable(self.item_size_warning_threshold))
|
410
|
+
|
411
|
+
for (server, fp, sock) in self._get_conns(cmd):
|
412
|
+
conn_start_time = tm.time()
|
284
413
|
try:
|
285
414
|
with Timeout(self._io_timeout):
|
286
|
-
sock.sendall(set_msg(
|
415
|
+
sock.sendall(set_msg(cmd.hash_key, flags, timeout, value))
|
287
416
|
# Wait for the set to complete
|
288
|
-
fp.readline()
|
417
|
+
msg = fp.readline().strip()
|
418
|
+
if msg != b'STORED':
|
419
|
+
msg = msg.decode('ascii')
|
420
|
+
raise MemcacheConnectionError('failed set: %s' % msg)
|
289
421
|
self._return_conn(server, fp, sock)
|
290
422
|
return
|
291
423
|
except (Exception, Timeout) as e:
|
292
|
-
self._exception_occurred(server, e,
|
293
|
-
|
294
|
-
|
424
|
+
self._exception_occurred(server, e, cmd, conn_start_time,
|
425
|
+
sock=sock, fp=fp)
|
426
|
+
if raise_on_error:
|
427
|
+
raise MemcacheConnectionError(
|
428
|
+
"No memcached connections succeeded.")
|
429
|
+
|
430
|
+
@memcached_timing_stats(sample_rate=TIMING_SAMPLE_RATE_MEDIUM)
|
431
|
+
def get(self, key, raise_on_error=False):
|
295
432
|
"""
|
296
433
|
Gets the object specified by key. It will also unserialize the object
|
297
|
-
before returning if it is serialized in memcache with JSON
|
298
|
-
is pickled and unpickling is allowed.
|
434
|
+
before returning if it is serialized in memcache with JSON.
|
299
435
|
|
300
436
|
:param key: key
|
437
|
+
:param raise_on_error: if True, propagate Timeouts and other errors.
|
438
|
+
By default, errors are treated as cache misses.
|
301
439
|
:returns: value of the key in memcache
|
302
440
|
"""
|
303
|
-
|
441
|
+
cmd = MemcacheCommand('get', key)
|
304
442
|
value = None
|
305
|
-
for (server, fp, sock) in self._get_conns(
|
443
|
+
for (server, fp, sock) in self._get_conns(cmd):
|
444
|
+
conn_start_time = tm.time()
|
306
445
|
try:
|
307
446
|
with Timeout(self._io_timeout):
|
308
|
-
sock.sendall(b'get ' +
|
447
|
+
sock.sendall(b'get ' + cmd.hash_key + b'\r\n')
|
309
448
|
line = fp.readline().strip().split()
|
310
449
|
while True:
|
311
450
|
if not line:
|
312
451
|
raise MemcacheConnectionError('incomplete read')
|
313
452
|
if line[0].upper() == b'END':
|
314
453
|
break
|
315
|
-
if line[0].upper() == b'VALUE' and
|
454
|
+
if (line[0].upper() == b'VALUE' and
|
455
|
+
line[1] == cmd.hash_key):
|
316
456
|
size = int(line[3])
|
317
457
|
value = fp.read(size)
|
318
458
|
if int(line[2]) & PICKLE_FLAG:
|
319
|
-
|
320
|
-
|
321
|
-
else:
|
322
|
-
value = None
|
323
|
-
elif int(line[2]) & JSON_FLAG:
|
459
|
+
value = None
|
460
|
+
if int(line[2]) & JSON_FLAG:
|
324
461
|
value = json.loads(value)
|
325
462
|
fp.readline()
|
326
463
|
line = fp.readline().strip().split()
|
327
464
|
self._return_conn(server, fp, sock)
|
328
465
|
return value
|
329
466
|
except (Exception, Timeout) as e:
|
330
|
-
self._exception_occurred(server, e,
|
331
|
-
|
467
|
+
self._exception_occurred(server, e, cmd, conn_start_time,
|
468
|
+
sock=sock, fp=fp)
|
469
|
+
if raise_on_error:
|
470
|
+
raise MemcacheConnectionError(
|
471
|
+
"No memcached connections succeeded.")
|
472
|
+
|
473
|
+
def _incr_or_decr(self, fp, sock, cmd, delta):
|
474
|
+
sock.sendall(b' '.join([cmd.command, cmd.hash_key, delta]) + b'\r\n')
|
475
|
+
line = fp.readline().strip().split()
|
476
|
+
if not line:
|
477
|
+
raise MemcacheConnectionError('incomplete read')
|
478
|
+
if line[0].upper() == b'NOT_FOUND':
|
479
|
+
return None
|
480
|
+
return int(line[0].strip())
|
481
|
+
|
482
|
+
def _add(self, fp, sock, cmd, add_val, timeout):
|
483
|
+
sock.sendall(b' '.join([
|
484
|
+
b'add', cmd.hash_key, b'0', str(timeout).encode('ascii'),
|
485
|
+
str(len(add_val)).encode('ascii')
|
486
|
+
]) + b'\r\n' + add_val + b'\r\n')
|
487
|
+
line = fp.readline().strip().split()
|
488
|
+
return None if line[0].upper() == b'NOT_STORED' else int(add_val)
|
489
|
+
|
490
|
+
@memcached_timing_stats(sample_rate=TIMING_SAMPLE_RATE_LOW)
|
332
491
|
def incr(self, key, delta=1, time=0):
|
333
492
|
"""
|
334
493
|
Increments a key which has a numeric value by delta.
|
@@ -346,44 +505,35 @@ class MemcacheRing(object):
|
|
346
505
|
:returns: result of incrementing
|
347
506
|
:raises MemcacheConnectionError:
|
348
507
|
"""
|
349
|
-
|
350
|
-
|
351
|
-
if delta < 0:
|
352
|
-
command = b'decr'
|
353
|
-
delta = str(abs(int(delta))).encode('ascii')
|
508
|
+
cmd = MemcacheCommand('incr' if delta >= 0 else 'decr', key)
|
509
|
+
delta_val = str(abs(int(delta))).encode('ascii')
|
354
510
|
timeout = sanitize_timeout(time)
|
355
|
-
for (server, fp, sock) in self._get_conns(
|
511
|
+
for (server, fp, sock) in self._get_conns(cmd):
|
512
|
+
conn_start_time = tm.time()
|
356
513
|
try:
|
357
514
|
with Timeout(self._io_timeout):
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
if line[0].upper() == b'NOT_STORED':
|
373
|
-
sock.sendall(b' '.join([
|
374
|
-
command, key, delta]) + b'\r\n')
|
375
|
-
line = fp.readline().strip().split()
|
376
|
-
ret = int(line[0].strip())
|
377
|
-
else:
|
378
|
-
ret = int(add_val)
|
379
|
-
else:
|
380
|
-
ret = int(line[0].strip())
|
515
|
+
new_val = self._incr_or_decr(fp, sock, cmd, delta_val)
|
516
|
+
if new_val is None:
|
517
|
+
add_val = b'0' if cmd.method == 'decr' else delta_val
|
518
|
+
new_val = self._add(fp, sock, cmd, add_val, timeout)
|
519
|
+
if new_val is None:
|
520
|
+
new_val = self._incr_or_decr(
|
521
|
+
fp, sock, cmd, delta_val)
|
522
|
+
if new_val is None:
|
523
|
+
# This can happen if this thread takes more
|
524
|
+
# than the TTL to get from the first failed
|
525
|
+
# incr to the second incr, during which time
|
526
|
+
# the key was concurrently added and expired.
|
527
|
+
raise MemcacheIncrNotFoundError(
|
528
|
+
'expired ttl=%s' % time)
|
381
529
|
self._return_conn(server, fp, sock)
|
382
|
-
return
|
530
|
+
return new_val
|
383
531
|
except (Exception, Timeout) as e:
|
384
|
-
self._exception_occurred(server, e,
|
385
|
-
|
532
|
+
self._exception_occurred(server, e, cmd, conn_start_time,
|
533
|
+
sock=sock, fp=fp)
|
534
|
+
raise MemcacheConnectionError("No memcached connections succeeded.")
|
386
535
|
|
536
|
+
@memcached_timing_stats(sample_rate=TIMING_SAMPLE_RATE_LOW)
|
387
537
|
def decr(self, key, delta=1, time=0):
|
388
538
|
"""
|
389
539
|
Decrements a key which has a numeric value by delta. Calls incr with
|
@@ -399,24 +549,32 @@ class MemcacheRing(object):
|
|
399
549
|
"""
|
400
550
|
return self.incr(key, delta=-delta, time=time)
|
401
551
|
|
402
|
-
|
552
|
+
@memcached_timing_stats(sample_rate=TIMING_SAMPLE_RATE_HIGH)
|
553
|
+
def delete(self, key, server_key=None):
|
403
554
|
"""
|
404
555
|
Deletes a key/value pair from memcache.
|
405
556
|
|
406
557
|
:param key: key to be deleted
|
558
|
+
:param server_key: key to use in determining which server in the ring
|
559
|
+
is used
|
407
560
|
"""
|
408
|
-
|
409
|
-
|
561
|
+
cmd = server_cmd = MemcacheCommand('delete', key)
|
562
|
+
if server_key:
|
563
|
+
server_cmd = MemcacheCommand('delete', server_key)
|
564
|
+
for (server, fp, sock) in self._get_conns(server_cmd):
|
565
|
+
conn_start_time = tm.time()
|
410
566
|
try:
|
411
567
|
with Timeout(self._io_timeout):
|
412
|
-
sock.sendall(b'delete ' +
|
568
|
+
sock.sendall(b'delete ' + cmd.hash_key + b'\r\n')
|
413
569
|
# Wait for the delete to complete
|
414
570
|
fp.readline()
|
415
571
|
self._return_conn(server, fp, sock)
|
416
572
|
return
|
417
573
|
except (Exception, Timeout) as e:
|
418
|
-
self._exception_occurred(server, e,
|
574
|
+
self._exception_occurred(server, e, cmd, conn_start_time,
|
575
|
+
sock=sock, fp=fp)
|
419
576
|
|
577
|
+
@memcached_timing_stats(sample_rate=TIMING_SAMPLE_RATE_HIGH)
|
420
578
|
def set_multi(self, mapping, server_key, serialize=True, time=0,
|
421
579
|
min_compress_len=0):
|
422
580
|
"""
|
@@ -426,30 +584,27 @@ class MemcacheRing(object):
|
|
426
584
|
:param server_key: key to use in determining which server in the ring
|
427
585
|
is used
|
428
586
|
:param serialize: if True, value is serialized with JSON before sending
|
429
|
-
to memcache
|
430
|
-
pickle instead of JSON (to avoid cache poisoning)
|
587
|
+
to memcache.
|
431
588
|
:param time: the time to live
|
432
589
|
:min_compress_len: minimum compress length, this parameter was added
|
433
590
|
to keep the signature compatible with
|
434
591
|
python-memcached interface. This implementation
|
435
592
|
ignores it
|
436
593
|
"""
|
437
|
-
|
594
|
+
cmd = MemcacheCommand('set_multi', server_key)
|
438
595
|
timeout = sanitize_timeout(time)
|
439
596
|
msg = []
|
440
597
|
for key, value in mapping.items():
|
441
598
|
key = md5hash(key)
|
442
599
|
flags = 0
|
443
|
-
if serialize
|
444
|
-
value = pickle.dumps(value, PICKLE_PROTOCOL)
|
445
|
-
flags |= PICKLE_FLAG
|
446
|
-
elif serialize:
|
600
|
+
if serialize:
|
447
601
|
if isinstance(value, bytes):
|
448
602
|
value = value.decode('utf8')
|
449
603
|
value = json.dumps(value).encode('ascii')
|
450
604
|
flags |= JSON_FLAG
|
451
605
|
msg.append(set_msg(key, flags, timeout, value))
|
452
|
-
for (server, fp, sock) in self._get_conns(
|
606
|
+
for (server, fp, sock) in self._get_conns(cmd):
|
607
|
+
conn_start_time = tm.time()
|
453
608
|
try:
|
454
609
|
with Timeout(self._io_timeout):
|
455
610
|
sock.sendall(b''.join(msg))
|
@@ -459,8 +614,10 @@ class MemcacheRing(object):
|
|
459
614
|
self._return_conn(server, fp, sock)
|
460
615
|
return
|
461
616
|
except (Exception, Timeout) as e:
|
462
|
-
self._exception_occurred(server, e,
|
617
|
+
self._exception_occurred(server, e, cmd, conn_start_time,
|
618
|
+
sock=sock, fp=fp)
|
463
619
|
|
620
|
+
@memcached_timing_stats(sample_rate=TIMING_SAMPLE_RATE_HIGH)
|
464
621
|
def get_multi(self, keys, server_key):
|
465
622
|
"""
|
466
623
|
Gets multiple values from memcache for the given keys.
|
@@ -470,12 +627,13 @@ class MemcacheRing(object):
|
|
470
627
|
is used
|
471
628
|
:returns: list of values
|
472
629
|
"""
|
473
|
-
|
474
|
-
|
475
|
-
for (server, fp, sock) in self._get_conns(
|
630
|
+
cmd = MemcacheCommand('get_multi', server_key)
|
631
|
+
hash_keys = [md5hash(key) for key in keys]
|
632
|
+
for (server, fp, sock) in self._get_conns(cmd):
|
633
|
+
conn_start_time = tm.time()
|
476
634
|
try:
|
477
635
|
with Timeout(self._io_timeout):
|
478
|
-
sock.sendall(b'get ' + b' '.join(
|
636
|
+
sock.sendall(b'get ' + b' '.join(hash_keys) + b'\r\n')
|
479
637
|
line = fp.readline().strip().split()
|
480
638
|
responses = {}
|
481
639
|
while True:
|
@@ -487,17 +645,14 @@ class MemcacheRing(object):
|
|
487
645
|
size = int(line[3])
|
488
646
|
value = fp.read(size)
|
489
647
|
if int(line[2]) & PICKLE_FLAG:
|
490
|
-
|
491
|
-
value = pickle.loads(value)
|
492
|
-
else:
|
493
|
-
value = None
|
648
|
+
value = None
|
494
649
|
elif int(line[2]) & JSON_FLAG:
|
495
650
|
value = json.loads(value)
|
496
651
|
responses[line[1]] = value
|
497
652
|
fp.readline()
|
498
653
|
line = fp.readline().strip().split()
|
499
654
|
values = []
|
500
|
-
for key in
|
655
|
+
for key in hash_keys:
|
501
656
|
if key in responses:
|
502
657
|
values.append(responses[key])
|
503
658
|
else:
|
@@ -505,4 +660,98 @@ class MemcacheRing(object):
|
|
505
660
|
self._return_conn(server, fp, sock)
|
506
661
|
return values
|
507
662
|
except (Exception, Timeout) as e:
|
508
|
-
self._exception_occurred(server, e,
|
663
|
+
self._exception_occurred(server, e, cmd, conn_start_time,
|
664
|
+
sock=sock, fp=fp)
|
665
|
+
|
666
|
+
|
667
|
+
def load_memcache(conf, logger):
|
668
|
+
"""
|
669
|
+
Build a MemcacheRing object from the given config. It will also use the
|
670
|
+
passed in logger.
|
671
|
+
|
672
|
+
:param conf: a dict, the config options
|
673
|
+
:param logger: a logger
|
674
|
+
"""
|
675
|
+
memcache_servers = conf.get('memcache_servers')
|
676
|
+
try:
|
677
|
+
# Originally, while we documented using memcache_max_connections
|
678
|
+
# we only accepted max_connections
|
679
|
+
max_conns = int(conf.get('memcache_max_connections',
|
680
|
+
conf.get('max_connections', 0)))
|
681
|
+
except ValueError:
|
682
|
+
max_conns = 0
|
683
|
+
|
684
|
+
memcache_options = {}
|
685
|
+
if (not memcache_servers
|
686
|
+
or max_conns <= 0):
|
687
|
+
path = os.path.join(conf.get('swift_dir', '/etc/swift'),
|
688
|
+
'memcache.conf')
|
689
|
+
memcache_conf = ConfigParser()
|
690
|
+
if memcache_conf.read(path):
|
691
|
+
# if memcache.conf exists we'll start with those base options
|
692
|
+
try:
|
693
|
+
memcache_options = dict(memcache_conf.items('memcache'))
|
694
|
+
except NoSectionError:
|
695
|
+
pass
|
696
|
+
|
697
|
+
if not memcache_servers:
|
698
|
+
try:
|
699
|
+
memcache_servers = \
|
700
|
+
memcache_conf.get('memcache', 'memcache_servers')
|
701
|
+
except (NoSectionError, NoOptionError):
|
702
|
+
pass
|
703
|
+
if max_conns <= 0:
|
704
|
+
try:
|
705
|
+
new_max_conns = \
|
706
|
+
memcache_conf.get('memcache',
|
707
|
+
'memcache_max_connections')
|
708
|
+
max_conns = int(new_max_conns)
|
709
|
+
except (NoSectionError, NoOptionError, ValueError):
|
710
|
+
pass
|
711
|
+
|
712
|
+
# while memcache.conf options are the base for the memcache
|
713
|
+
# middleware, if you set the same option also in the filter
|
714
|
+
# section of the proxy config it is more specific.
|
715
|
+
memcache_options.update(conf)
|
716
|
+
connect_timeout = float(memcache_options.get(
|
717
|
+
'connect_timeout', CONN_TIMEOUT))
|
718
|
+
pool_timeout = float(memcache_options.get(
|
719
|
+
'pool_timeout', POOL_TIMEOUT))
|
720
|
+
tries = int(memcache_options.get('tries', TRY_COUNT))
|
721
|
+
io_timeout = float(memcache_options.get('io_timeout', IO_TIMEOUT))
|
722
|
+
if config_true_value(memcache_options.get('tls_enabled', 'false')):
|
723
|
+
tls_cafile = memcache_options.get('tls_cafile')
|
724
|
+
tls_certfile = memcache_options.get('tls_certfile')
|
725
|
+
tls_keyfile = memcache_options.get('tls_keyfile')
|
726
|
+
tls_context = ssl.create_default_context(
|
727
|
+
cafile=tls_cafile)
|
728
|
+
if tls_certfile:
|
729
|
+
tls_context.load_cert_chain(tls_certfile, tls_keyfile)
|
730
|
+
else:
|
731
|
+
tls_context = None
|
732
|
+
error_suppression_interval = float(memcache_options.get(
|
733
|
+
'error_suppression_interval', ERROR_LIMIT_TIME))
|
734
|
+
error_suppression_limit = float(memcache_options.get(
|
735
|
+
'error_suppression_limit', ERROR_LIMIT_COUNT))
|
736
|
+
item_size_warning_threshold = int(memcache_options.get(
|
737
|
+
'item_size_warning_threshold', DEFAULT_ITEM_SIZE_WARNING_THRESHOLD))
|
738
|
+
|
739
|
+
if not memcache_servers:
|
740
|
+
memcache_servers = '127.0.0.1:11211'
|
741
|
+
if max_conns <= 0:
|
742
|
+
max_conns = 2
|
743
|
+
|
744
|
+
return MemcacheRing(
|
745
|
+
[s.strip() for s in memcache_servers.split(',')
|
746
|
+
if s.strip()],
|
747
|
+
connect_timeout=connect_timeout,
|
748
|
+
pool_timeout=pool_timeout,
|
749
|
+
tries=tries,
|
750
|
+
io_timeout=io_timeout,
|
751
|
+
max_conns=max_conns,
|
752
|
+
tls_context=tls_context,
|
753
|
+
logger=logger,
|
754
|
+
error_limit_count=error_suppression_limit,
|
755
|
+
error_limit_time=error_suppression_interval,
|
756
|
+
error_limit_duration=error_suppression_interval,
|
757
|
+
item_size_warning_threshold=item_size_warning_threshold)
|