Repository: rucio/rucio

Issue:
exception in attach_collections_to_containers
Motivation
----------
```
2022-07-08 06:32:43,759\troot\t31\tDEBUG\tUncaught RucioException in rucio.web.rest.flaskapi.v1.dids Attachment POST
Traceback (most recent call last):
  File "/usr/lib64/python3.6/configparser.py", line 1138, in _unify_values
    sectiondict = self._sections[section]
KeyError: 'hermes'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/rucio/common/config.py", line 58, in config_get
    return extract_function(get_config(), section, option)
  File "/usr/lib64/python3.6/configparser.py", line 781, in get
    d = self._unify_values(section, vars)
  File "/usr/lib64/python3.6/configparser.py", line 1141, in _unify_values
    raise NoSectionError(section)
configparser.NoSectionError: No section: 'hermes'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/base.py", line 1803, in _execute_context
    cursor, statement, parameters, context
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/default.py", line 732, in do_execute
    cursor.execute(statement, parameters)
cx_Oracle.IntegrityError: ORA-00001: unique constraint (ATLAS_RUCIO.CONTENTS_PK) violated

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/rucio/db/sqla/session.py", line 388, in new_funct
    result = function(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rucio/api/did.py", line 227, in attach_dids
    account=attachment.get('account', issuer), session=session)
  File "/usr/local/lib/python3.6/site-packages/rucio/db/sqla/session.py", line 402, in new_funct
    result = function(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rucio/core/did.py", line 262, in attach_dids
    return attach_dids_to_dids(attachments=[{'scope': scope, 'name': name, 'dids': dids, 'rse_id': rse_id}], account=account, session=session)
  File "/usr/local/lib/python3.6/site-packages/rucio/db/sqla/session.py", line 402, in new_funct
    result = function(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rucio/core/did.py", line 282, in attach_dids_to_dids
    return _attach_dids_to_dids(attachments=attachments, account=account, ignore_duplicate=ignore_duplicate, session=session)
  File "/usr/local/lib/python3.6/site-packages/rucio/db/sqla/session.py", line 402, in new_funct
    result = function(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rucio/core/did.py", line 351, in _attach_dids_to_dids
    session=session)
  File "/usr/local/lib/python3.6/site-packages/rucio/db/sqla/session.py", line 402, in new_funct
    result = function(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rucio/core/did.py", line 726, in __add_collections_to_container
    add_message('REGISTER_CNT', message, session=session)
  File "/usr/local/lib/python3.6/site-packages/rucio/db/sqla/session.py", line 402, in new_funct
    result = function(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rucio/core/message.py", line 46, in add_message
    for service in config_get_list('hermes', 'services_list', raise_exception=False, default='activemq,email', session=session):
  File "/usr/local/lib/python3.6/site-packages/rucio/common/config.py", line 280, in config_get_list
    extract_function=ConfigParser.ConfigParser.get
  File "/usr/local/lib/python3.6/site-packages/rucio/common/config.py", line 74, in config_get
    use_cache=use_cache, expiration_time=expiration_time)
  File "/usr/local/lib/python3.6/site-packages/rucio/common/config.py", line 318, in __config_get_table
    expiration_time=expiration_time)
  File "/usr/local/lib/python3.6/site-packages/retrying.py", line 49, in wrapped_f
    return Retrying(*dargs, **dkw).call(f, *args, **kw)
  File "/usr/local/lib/python3.6/site-packages/retrying.py", line 206, in call
    return attempt.get(self._wrap_exception)
  File "/usr/local/lib/python3.6/site-packages/retrying.py", line 247, in get
    six.reraise(self.value[0], self.value[1], self.value[2])
  File "/usr/local/lib/python3.6/site-packages/six.py", line 719, in reraise
    raise value
  File "/usr/local/lib/python3.6/site-packages/retrying.py", line 200, in call
    attempt = Attempt(fn(*args, **kwargs), attempt_number, False)
  File "/usr/local/lib/python3.6/site-packages/rucio/db/sqla/session.py", line 322, in new_funct
    return function(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rucio/core/config.py", line 171, in get
    tmp = session.query(models.Config.value).filter_by(section=section, opt=option).first()
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/query.py", line 2810, in first
    return self.limit(1)._iter().first()
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/query.py", line 2897, in _iter
    execution_options={"_sa_orm_load_options": self.load_options},
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/session.py", line 1646, in execute
    _parent_execute_state is not None,
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/context.py", line 318, in orm_pre_session_exec
    session._autoflush()
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/session.py", line 2237, in _autoflush
    util.raise_(e, with_traceback=sys.exc_info()[2])
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/util/compat.py", line 207, in raise_
    raise exception
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/session.py", line 2226, in _autoflush
    self.flush()
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/session.py", line 3363, in flush
    self._flush(objects)
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/session.py", line 3503, in _flush
    transaction.rollback(_capture_exception=True)
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/util/langhelpers.py", line 72, in __exit__
    with_traceback=exc_tb,
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/util/compat.py", line 207, in raise_
    raise exception
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/session.py", line 3463, in _flush
    flush_context.execute()
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/unitofwork.py", line 456, in execute
    rec.execute(self)
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/unitofwork.py", line 633, in execute
    uow,
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/persistence.py", line 249, in save_obj
    insert,
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/persistence.py", line 1097, in _emit_insert_statements
    statement, multiparams, execution_options=execution_options
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/base.py", line 1614, in _execute_20
    return meth(self, args_10style, kwargs_10style, execution_options)
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/sql/elements.py", line 326, in _execute_on_connection
    self, multiparams, params, execution_options
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/base.py", line 1491, in _execute_clauseelement
    cache_hit=cache_hit,
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/base.py", line 1846, in _execute_context
    e, statement, parameters, cursor, context
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/base.py", line 2027, in _handle_dbapi_exception
    sqlalchemy_exception, with_traceback=exc_info[2], from_=e
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/util/compat.py", line 207, in raise_
    raise exception
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/base.py", line 1803, in _execute_context
    cursor, statement, parameters, context
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/default.py", line 732, in do_execute
    cursor.execute(statement, parameters)
sqlalchemy.exc.IntegrityError: (raised as a result of Query-invoked autoflush; consider using a session.no_autoflush block if this flush is occurring prematurely)
(cx_Oracle.IntegrityError) ORA-00001: unique constraint (ATLAS_RUCIO.CONTENTS_PK) violated
[SQL: INSERT INTO atlas_rucio.contents (scope, name, child_scope, child_name, did_type, child_type, bytes, adler32, md5, guid, events, rule_evaluation, created_at, updated_at) VALUES (:scope, :name, :child_scope, :child_name, :did_type, :child_type, :bytes, :adler32, :md5, :guid, :events, :rule_evaluation, :created_at, :updated_at)]
[parameters: ................
(Background on this error at: https://sqlalche.me/e/14/gkpj)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/rucio/web/rest/flaskapi/v1/common.py", line 54, in dispatch_request
    return super(ErrorHandlingMethodView, self).dispatch_request(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/flask/views.py", line 158, in dispatch_request
    return current_app.ensure_sync(meth)(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rucio/web/rest/flaskapi/v1/dids.py", line 978, in post
    attach_dids(scope=scope, name=name, attachment=attachments, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
  File "/usr/local/lib/python3.6/site-packages/rucio/db/sqla/session.py", line 395, in new_funct
    raise DatabaseException(str(error))
rucio.common.exception.DatabaseException: Database exception.
Details: (raised as a result of Query-invoked autoflush; consider using a session.no_autoflush block if this flush is occurring prematurely)
(cx_Oracle.IntegrityError) ORA-00001: unique constraint (ATLAS_RUCIO.CONTENTS_PK) violated
[SQL: INSERT INTO atlas_rucio.contents (scope, name, child_scope, child_name, did_type, child_type, bytes, adler32, md5, guid, events, rule_evaluation, created_at, updated_at) VALUES (:scope, :name, :child_scope, :child_name, :did_type, :child_type, :bytes, :adler32, :md5, :guid, :events, :rule_evaluation, :created_at, :updated_at)]
[parameters: ...................
(Background on this error at: https://sqlalche.me/e/14/gkpj)

```




Discussion:
This also happens in the non-temp-table version:
```
2022-07-08 07:25:55,316\troot\t26\tDEBUG\tUncaught RucioException in rucio.web.rest.flaskapi.v1.dids Attachment POST
Traceback (most recent call last):
  File "/usr/lib64/python3.6/configparser.py", line 1138, in _unify_values
    sectiondict = self._sections[section]
KeyError: 'hermes'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/rucio/common/config.py", line 58, in config_get
    return extract_function(get_config(), section, option)
  File "/usr/lib64/python3.6/configparser.py", line 781, in get
    d = self._unify_values(section, vars)
  File "/usr/lib64/python3.6/configparser.py", line 1141, in _unify_values
    raise NoSectionError(section)
configparser.NoSectionError: No section: 'hermes'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/base.py", line 1803, in _execute_context
    cursor, statement, parameters, context
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/default.py", line 732, in do_execute
    cursor.execute(statement, parameters)
cx_Oracle.IntegrityError: ORA-00001: unique constraint (ATLAS_RUCIO.CONTENTS_PK) violated

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/rucio/db/sqla/session.py", line 388, in new_funct
    result = function(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rucio/api/did.py", line 227, in attach_dids
    account=attachment.get('account', issuer), session=session)
  File "/usr/local/lib/python3.6/site-packages/rucio/db/sqla/session.py", line 402, in new_funct
    result = function(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rucio/core/did.py", line 262, in attach_dids
    return attach_dids_to_dids(attachments=[{'scope': scope, 'name': name, 'dids': dids, 'rse_id': rse_id}], account=account, session=session)
  File "/usr/local/lib/python3.6/site-packages/rucio/db/sqla/session.py", line 402, in new_funct
    result = function(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rucio/core/did.py", line 284, in attach_dids_to_dids
    return _attach_dids_to_dids_without_temp_tables(attachments=attachments, account=account, ignore_duplicate=ignore_duplicate, session=session)
  File "/usr/local/lib/python3.6/site-packages/rucio/db/sqla/session.py", line 402, in new_funct
    result = function(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rucio/core/did.py", line 1156, in _attach_dids_to_dids_without_temp_tables
    account=account, session=session)
  File "/usr/local/lib/python3.6/site-packages/rucio/db/sqla/session.py", line 402, in new_funct
    result = function(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rucio/core/did.py", line 1087, in __add_collections_to_container_without_temp_tables
    add_message('REGISTER_CNT', message, session=session)
  File "/usr/local/lib/python3.6/site-packages/rucio/db/sqla/session.py", line 402, in new_funct
    result = function(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rucio/core/message.py", line 46, in add_message
    for service in config_get_list('hermes', 'services_list', raise_exception=False, default='activemq,email', session=session):
  File "/usr/local/lib/python3.6/site-packages/rucio/common/config.py", line 280, in config_get_list
    extract_function=ConfigParser.ConfigParser.get
  File "/usr/local/lib/python3.6/site-packages/rucio/common/config.py", line 74, in config_get
    use_cache=use_cache, expiration_time=expiration_time)
  File "/usr/local/lib/python3.6/site-packages/rucio/common/config.py", line 318, in __config_get_table
    expiration_time=expiration_time)
  File "/usr/local/lib/python3.6/site-packages/retrying.py", line 49, in wrapped_f
    return Retrying(*dargs, **dkw).call(f, *args, **kw)
  File "/usr/local/lib/python3.6/site-packages/retrying.py", line 206, in call
    return attempt.get(self._wrap_exception)
  File "/usr/local/lib/python3.6/site-packages/retrying.py", line 247, in get
    six.reraise(self.value[0], self.value[1], self.value[2])
  File "/usr/local/lib/python3.6/site-packages/six.py", line 719, in reraise
    raise value
  File "/usr/local/lib/python3.6/site-packages/retrying.py", line 200, in call
    attempt = Attempt(fn(*args, **kwargs), attempt_number, False)
  File "/usr/local/lib/python3.6/site-packages/rucio/db/sqla/session.py", line 322, in new_funct
    return function(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rucio/core/config.py", line 171, in get
    tmp = session.query(models.Config.value).filter_by(section=section, opt=option).first()
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/query.py", line 2810, in first
    return self.limit(1)._iter().first()
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/query.py", line 2897, in _iter
    execution_options={"_sa_orm_load_options": self.load_options},
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/session.py", line 1646, in execute
    _parent_execute_state is not None,
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/context.py", line 318, in orm_pre_session_exec
    session._autoflush()
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/session.py", line 2237, in _autoflush
    util.raise_(e, with_traceback=sys.exc_info()[2])
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/util/compat.py", line 207, in raise_
    raise exception
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/session.py", line 2226, in _autoflush
    self.flush()
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/session.py", line 3363, in flush
    self._flush(objects)
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/session.py", line 3503, in _flush
    transaction.rollback(_capture_exception=True)
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/util/langhelpers.py", line 72, in __exit__
    with_traceback=exc_tb,
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/util/compat.py", line 207, in raise_
    raise exception
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/session.py", line 3463, in _flush
    flush_context.execute()
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/unitofwork.py", line 456, in execute
    rec.execute(self)
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/unitofwork.py", line 633, in execute
    uow,
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/persistence.py", line 249, in save_obj
    insert,
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/persistence.py", line 1097, in _emit_insert_statements
    statement, multiparams, execution_options=execution_options
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/base.py", line 1614, in _execute_20
    return meth(self, args_10style, kwargs_10style, execution_options)
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/sql/elements.py", line 326, in _execute_on_connection
    self, multiparams, params, execution_options
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/base.py", line 1491, in _execute_clauseelement
    cache_hit=cache_hit,
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/base.py", line 1846, in _execute_context
    e, statement, parameters, cursor, context
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/base.py", line 2027, in _handle_dbapi_exception
    sqlalchemy_exception, with_traceback=exc_info[2], from_=e
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/util/compat.py", line 207, in raise_
    raise exception
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/base.py", line 1803, in _execute_context
    cursor, statement, parameters, context
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/default.py", line 732, in do_execute
    cursor.execute(statement, parameters)
sqlalchemy.exc.IntegrityError: (raised as a result of Query-invoked autoflush; consider using a session.no_autoflush block if this flush is occurring prematurely)
(cx_Oracle.IntegrityError) ORA-00001: unique constraint (ATLAS_RUCIO.CONTENTS_PK) violated
[SQL: INSERT INTO atlas_rucio.contents (scope, name, child_scope, child_name, did_type, child_type, bytes, adler32, md5, guid, events, rule_evaluation, created_at, updated_at) VALUES (:scope, :name, :child_scope, :child_name, :did_type, :child_type, :bytes, :adler32, :md5, :guid, :events, :rule_evaluation, :created_at, :updated_at)]
[parameters: ....................
(Background on this error at: https://sqlalche.me/e/14/gkpj)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/rucio/web/rest/flaskapi/v1/common.py", line 54, in dispatch_request
    return super(ErrorHandlingMethodView, self).dispatch_request(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/flask/views.py", line 158, in dispatch_request
    return current_app.ensure_sync(meth)(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rucio/web/rest/flaskapi/v1/dids.py", line 978, in post
    attach_dids(scope=scope, name=name, attachment=attachments, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
  File "/usr/local/lib/python3.6/site-packages/rucio/db/sqla/session.py", line 395, in new_funct
    raise DatabaseException(str(error))
rucio.common.exception.DatabaseException: Database exception.
Details: (raised as a result of Query-invoked autoflush; consider using a session.no_autoflush block if this flush is occurring prematurely)
(cx_Oracle.IntegrityError) ORA-00001: unique constraint (ATLAS_RUCIO.CONTENTS_PK) violated
[SQL: INSERT INTO atlas_rucio.contents (scope, name, child_scope, child_name, did_type, child_type, bytes, adler32, md5, guid, events, rule_evaluation, created_at, updated_at) VALUES (:scope, :name, :child_scope, :child_name, :did_type, :child_type, :bytes, :adler32, :md5, :guid, :events, :rule_evaluation, :created_at, :updated_at)]
[parameters: ...........
(Background on this error at: https://sqlalche.me/e/14/gkpj)

```
~~Also: to investigate why is there "retrying" in the call stack. Do we call attach_dids somehwere with @read_session ?~~

Retrying is on config_get. But is it a good idea to retry inside a transactional session ? Shouldn't the retrying be used only if the session is created by the read_session decorator and avoid retrying when the session is passed-through ? 
Where is the `config_get` retry issued? I am wondering why, within the core, we need to retry at all.

Resolving patch:
diff --git a/lib/rucio/core/did.py b/lib/rucio/core/did.py
--- a/lib/rucio/core/did.py
+++ b/lib/rucio/core/did.py
@@ -695,6 +695,7 @@ def __add_collections_to_container(parent_did, collections_temp_table, collectio
             if (row.scope, row.name) in container_parents:
                 raise exception.UnsupportedOperation('Circular attachment detected. %s:%s is already a parent of %s:%s' % (row.scope, row.name, parent_did.scope, parent_did.name))
 
+    messages = []
     for c in collections.values():
         did_asso = models.DataIdentifierAssociation(
             scope=parent_did.scope,
@@ -722,9 +723,11 @@ def __add_collections_to_container(parent_did, collections_temp_table, collectio
                    'childtype': chld_type}
         if account.vo != 'def':
             message['vo'] = account.vo
+        messages.append(message)
 
-        add_message('REGISTER_CNT', message, session=session)
     try:
+        for message in messages:
+            add_message('REGISTER_CNT', message, session=session)
         session.flush()
     except IntegrityError as error:
         if match('.*IntegrityError.*ORA-02291: integrity constraint .*CONTENTS_CHILD_ID_FK.*violated - parent key not found.*', error.args[0]) \
@@ -1056,6 +1059,7 @@ def __add_collections_to_container_without_temp_tables(scope, name, collections,
             if (row.scope, row.name) in container_parents:
                 raise exception.UnsupportedOperation('Circular attachment detected. %s:%s is already a parent of %s:%s', row.scope, row.name, scope, name)
 
+    messages = []
     for c in collections:
         did_asso = models.DataIdentifierAssociation(
             scope=scope,
@@ -1083,9 +1087,11 @@ def __add_collections_to_container_without_temp_tables(scope, name, collections,
                    'childtype': chld_type}
         if account.vo != 'def':
             message['vo'] = account.vo
+        messages.append(message)
 
-        add_message('REGISTER_CNT', message, session=session)
     try:
+        for message in messages:
+            add_message('REGISTER_CNT', message, session=session)
         session.flush()
     except IntegrityError as error:
         if match('.*IntegrityError.*ORA-02291: integrity constraint .*CONTENTS_CHILD_ID_FK.*violated - parent key not found.*', error.args[0]) \


Test patch: