proxy: Reduce round-trips to memcache and backend on info misses
Following a memcache restart in a SAIO, I've seen the following happen during an object HEAD: - etag_quoter wants to get account/container info to decide whether to quote-wrap or not - account info is a cache miss, so we make a no-auth'ed HEAD to the next filter in the pipeline - eventually this gets down to ratelimit, which *also* wants to get account info - still a cache miss, so we make a *separate* HEAD that eventually talks to the backend and populates cache - ratelimit realizes it can't ratelimit the request and lets the original HEAD through to the backend There's a related bug about how something similar can happen when the backend gets overloaded, but *everything is working* -- we just ought to be talking straight to the proxy app. Note that there's likely something similar going on with container info, but the hardcoded 10% sampling rate makes it harder to see if you're monitoring raw metric streams. I thought I fixed this in the related change, but no :-/ Change-Id: I49447c62abf9375541f396f984c91e128b8a05d5 Related-Change: If9249a42b30e2a2e7c4b0b91f947f24bf891b86f Related-Bug: #1883214
This commit is contained in:
parent
2a90eadc0d
commit
b68cc893f7
@ -438,6 +438,11 @@ def get_container_info(env, app, swift_source=None):
|
|||||||
account = wsgi_to_str(wsgi_account)
|
account = wsgi_to_str(wsgi_account)
|
||||||
container = wsgi_to_str(wsgi_container)
|
container = wsgi_to_str(wsgi_container)
|
||||||
|
|
||||||
|
# Try to cut through all the layers to the proxy app
|
||||||
|
try:
|
||||||
|
app = app._pipeline_final_app
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
# Check in environment cache and in memcache (in that order)
|
# Check in environment cache and in memcache (in that order)
|
||||||
info = _get_info_from_caches(app, env, account, container)
|
info = _get_info_from_caches(app, env, account, container)
|
||||||
|
|
||||||
@ -526,6 +531,11 @@ def get_account_info(env, app, swift_source=None):
|
|||||||
|
|
||||||
account = wsgi_to_str(wsgi_account)
|
account = wsgi_to_str(wsgi_account)
|
||||||
|
|
||||||
|
# Try to cut through all the layers to the proxy app
|
||||||
|
try:
|
||||||
|
app = app._pipeline_final_app
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
# Check in environment cache and in memcache (in that order)
|
# Check in environment cache and in memcache (in that order)
|
||||||
info = _get_info_from_caches(app, env, account)
|
info = _get_info_from_caches(app, env, account)
|
||||||
|
|
||||||
|
@ -152,7 +152,10 @@ class ZeroCacheDynamicResponseFactory(DynamicResponseFactory):
|
|||||||
class FakeApp(object):
|
class FakeApp(object):
|
||||||
|
|
||||||
recheck_container_existence = 30
|
recheck_container_existence = 30
|
||||||
|
container_existence_skip_cache = 0
|
||||||
recheck_account_existence = 30
|
recheck_account_existence = 30
|
||||||
|
account_existence_skip_cache = 0
|
||||||
|
logger = None
|
||||||
|
|
||||||
def __init__(self, response_factory=None, statuses=None):
|
def __init__(self, response_factory=None, statuses=None):
|
||||||
self.responses = response_factory or \
|
self.responses = response_factory or \
|
||||||
@ -352,6 +355,29 @@ class TestFuncs(BaseTest):
|
|||||||
self.assertEqual([e['swift.source'] for e in app.captured_envs],
|
self.assertEqual([e['swift.source'] for e in app.captured_envs],
|
||||||
['MC', 'MC'])
|
['MC', 'MC'])
|
||||||
|
|
||||||
|
def test_get_container_info_in_pipeline(self):
|
||||||
|
final_app = FakeApp()
|
||||||
|
|
||||||
|
def factory(app):
|
||||||
|
def wsgi_filter(env, start_response):
|
||||||
|
# lots of middlewares get info...
|
||||||
|
if env['PATH_INFO'].count('/') > 2:
|
||||||
|
get_container_info(env, app)
|
||||||
|
else:
|
||||||
|
get_account_info(env, app)
|
||||||
|
# ...then decide to no-op based on the result
|
||||||
|
return app(env, start_response)
|
||||||
|
|
||||||
|
wsgi_filter._pipeline_final_app = final_app
|
||||||
|
return wsgi_filter
|
||||||
|
|
||||||
|
# build up a pipeline
|
||||||
|
filtered_app = factory(factory(factory(final_app)))
|
||||||
|
req = Request.blank("/v1/a/c/o", environ={'swift.cache': FakeCache()})
|
||||||
|
req.get_response(filtered_app)
|
||||||
|
self.assertEqual([e['PATH_INFO'] for e in final_app.captured_envs],
|
||||||
|
['/v1/a', '/v1/a/c', '/v1/a/c/o'])
|
||||||
|
|
||||||
def test_get_object_info_swift_source(self):
|
def test_get_object_info_swift_source(self):
|
||||||
app = FakeApp()
|
app = FakeApp()
|
||||||
req = Request.blank("/v1/a/c/o",
|
req = Request.blank("/v1/a/c/o",
|
||||||
|
@ -513,7 +513,8 @@ class TestController(unittest.TestCase):
|
|||||||
|
|
||||||
def test_get_account_info_returns_values_as_strings(self):
|
def test_get_account_info_returns_values_as_strings(self):
|
||||||
app = mock.MagicMock()
|
app = mock.MagicMock()
|
||||||
app._pipeline_final_app.account_existence_skip_cache = 0.0
|
app._pipeline_final_app = app
|
||||||
|
app.account_existence_skip_cache = 0.0
|
||||||
memcache = mock.MagicMock()
|
memcache = mock.MagicMock()
|
||||||
memcache.get = mock.MagicMock()
|
memcache.get = mock.MagicMock()
|
||||||
memcache.get.return_value = {
|
memcache.get.return_value = {
|
||||||
@ -539,7 +540,8 @@ class TestController(unittest.TestCase):
|
|||||||
|
|
||||||
def test_get_container_info_returns_values_as_strings(self):
|
def test_get_container_info_returns_values_as_strings(self):
|
||||||
app = mock.MagicMock()
|
app = mock.MagicMock()
|
||||||
app._pipeline_final_app.container_existence_skip_cache = 0.0
|
app._pipeline_final_app = app
|
||||||
|
app.container_existence_skip_cache = 0.0
|
||||||
memcache = mock.MagicMock()
|
memcache = mock.MagicMock()
|
||||||
memcache.get = mock.MagicMock()
|
memcache.get = mock.MagicMock()
|
||||||
memcache.get.return_value = {
|
memcache.get.return_value = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user