attachment_swift: share a session for all connections

OVH's Swift applies a rate limit on the authentication.

attachment_swift authenticates again each time it has to read/write an
attachment. When running upgrades on upgrades of files or installing a
new DB, at some point, we get rejected with HTTP 429.

This commit introduces a shared storage for Swift Session. All
connections will reuses the same authentication token created the first
time a connection needs a Session.

Note: needs python-swiftclient>=3.7.0 to have
https://github.com/openstack/python-swiftclient/commit/1971ef880ff225379d4a91f00f89f323a1605eeb
This commit is contained in:
Guewen Baconnier
2019-05-02 16:13:04 +02:00
parent 85d32e44c6
commit 4e919d85cb
2 changed files with 65 additions and 10 deletions
+58 -4
View File
@@ -14,6 +14,9 @@ _logger = logging.getLogger(__name__)
try: try:
import swiftclient import swiftclient
import keystoneauth1
import keystoneauth1.identity
import keystoneauth1.session
from swiftclient.exceptions import ClientException from swiftclient.exceptions import ClientException
except ImportError: except ImportError:
swiftclient = None swiftclient = None
@@ -21,6 +24,54 @@ except ImportError:
_logger.debug("Cannot 'import swiftclient'.") _logger.debug("Cannot 'import swiftclient'.")
SWIFT_TIMEOUT = 15
class SwiftSessionStore(object):
"""Keep in memory the current Swift Auth session
The auth endpoint has a rate limit on swift, if every operation
on the filestore authenticate, the limit is exhausted and
operations rejected with an HTTP error code 429.
Swift connections can reuse the same session by asking a session
matching their connection parameters with ``get_session``.
The keystoneauth1's session automatically creates a new token
if the previous one is expired.
The best documentation I found about sessions is
https://docs.openstack.org/keystoneauth/latest/using-sessions.html
"""
def __init__(self):
self._sessions = {}
def _get_key(self, auth_url, username, password, tenant_name):
return (auth_url, username, password, tenant_name)
def get_session(self, auth_url=None, username=None, password=None,
tenant_name=None):
key = self._get_key(auth_url, username, password, tenant_name)
session = self._sessions.get(key)
if not session:
auth = keystoneauth1.identity.v2.Password(
username=username,
password=password,
tenant_name=tenant_name,
auth_url=auth_url,
)
session = keystoneauth1.session.Session(
auth=auth,
timeout=SWIFT_TIMEOUT,
)
self._sessions[key] = session
return session
swift_session_store = SwiftSessionStore()
class IrAttachment(models.Model): class IrAttachment(models.Model):
_inherit = 'ir.attachment' _inherit = 'ir.attachment'
@@ -47,11 +98,14 @@ class IrAttachment(models.Model):
"SWIFT_TENANT_NAME) properly set?" "SWIFT_TENANT_NAME) properly set?"
)) ))
try: try:
conn = swiftclient.client.Connection(authurl=host, session = swift_session_store.get_session(
user=account, username=account,
key=password, password=password,
tenant_name=tenant_name, tenant_name=tenant_name,
auth_version='2.0', auth_url=host,
)
conn = swiftclient.client.Connection(
session=session,
os_options=os_options, os_options=os_options,
) )
except ClientException: except ClientException:
+3 -2
View File
@@ -2,5 +2,6 @@ boto==2.42.0
redis==2.10.5 redis==2.10.5
python-json-logger==0.1.5 python-json-logger==0.1.5
statsd==3.2.1 statsd==3.2.1
python-swiftclient==3.4.0 python-swiftclient==3.7.0
python-keystoneclient==3.13.0 python-keystoneclient==3.19.0
keystoneauth1==3.14.0