Repository: rucio/rucio

Issue:
global temp table detection query is costly on oracle
Motivation
----------
It shows up in one of the top queries.


Modification
------------

Move the caching to memcached to share between threads and increase the cache duration





Discussion:

Resolving patch:
diff --git a/lib/rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py b/lib/rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py
--- a/lib/rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py
+++ b/lib/rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py
@@ -39,14 +39,14 @@ def upgrade():
         }
         for idx in range(5):
             create_table(
-                f'temporary_scope_name_{idx}',
+                f'TEMPORARY_SCOPE_NAME_{idx}',
                 sa.Column("scope", InternalScopeString(get_schema_value('SCOPE_LENGTH'))),
                 sa.Column("name", String(get_schema_value('NAME_LENGTH'))),
                 sa.PrimaryKeyConstraint('scope', 'name', name=f'TEMPORARY_SCOPE_NAME_{idx}_PK'),
                 **additional_kwargs,
             )
             create_table(
-                f'temporary_association_{idx}',
+                f'TEMPORARY_ASSOCIATION_{idx}',
                 sa.Column("scope", InternalScopeString(get_schema_value('SCOPE_LENGTH'))),
                 sa.Column("name", String(get_schema_value('NAME_LENGTH'))),
                 sa.Column("child_scope", InternalScopeString(get_schema_value('SCOPE_LENGTH'))),
@@ -55,7 +55,7 @@ def upgrade():
                 **additional_kwargs,
             )
             create_table(
-                f'temporary_id_{idx}',
+                f'TEMPORARY_ID_{idx}',
                 sa.Column("id", GUID()),
                 sa.PrimaryKeyConstraint('id', name=f'TEMPORARY_ID_{idx}_PK'),
                 **additional_kwargs,
@@ -70,6 +70,6 @@ def downgrade():
     if context.get_context().dialect.name == 'oracle':
         global_temp_tables = sa.inspect(context.get_bind()).get_temp_table_names()
         for idx in range(5):
-            for table_name in [f'temporary_id_{idx}', f'temporary_association_{idx}', f'temporary_scope_name_{idx}']:
+            for table_name in [f'TEMPORARY_ID_{idx}', f'TEMPORARY_ASSOCIATION_{idx}', f'TEMPORARY_SCOPE_NAME_{idx}']:
                 if table_name in global_temp_tables:
                     drop_table(table_name)
diff --git a/lib/rucio/db/sqla/util.py b/lib/rucio/db/sqla/util.py
--- a/lib/rucio/db/sqla/util.py
+++ b/lib/rucio/db/sqla/util.py
@@ -312,7 +312,19 @@ def list_oracle_global_temp_tables(session):
     cache_key = 'oracle_global_temp_tables'
     global_temp_tables = REGION.get(cache_key)
     if isinstance(global_temp_tables, NoValue):
-        global_temp_tables = inspect(session.bind).get_temp_table_names()
+        # As of time of writing, get_temp_table_names doesn't allow setting the correct schema when called
+        # (like get_table_names allows). This may be fixed in a later version of sqlalchemy:
+        # FIXME: substitute with something like this:
+        # global_temp_tables = [t.upper() for t in inspect(session.bind).get_temp_table_names()]
+        global_temp_tables = [
+            str(t[0]).upper()
+            for t in session.execute(
+                text('SELECT UPPER(table_name) '
+                     'FROM all_tables '
+                     'WHERE OWNER = :owner AND IOT_NAME IS NULL AND DURATION IS NOT NULL'),
+                dict(owner=models.BASE.metadata.schema.upper())
+            )
+        ]
         REGION.set(cache_key, global_temp_tables)
     return global_temp_tables
 
@@ -350,7 +362,7 @@ def _create_temp_table(name, *columns, primary_key=None, oracle_global_name=None
         global_temp_tables = list_oracle_global_temp_tables(session=session)
         if oracle_global_name is None:
             oracle_global_name = name
-        if oracle_global_name in global_temp_tables:
+        if oracle_global_name.upper() in global_temp_tables:
             oracle_table_is_global = True
             additional_kwargs = {
                 'oracle_on_commit': 'DELETE ROWS',
@@ -383,7 +395,7 @@ def _create_temp_table(name, *columns, primary_key=None, oracle_global_name=None
         oracle_global_name if oracle_table_is_global else name,
         base.metadata,
         *columns,
-        schema=None,  # Temporary tables exist in a special schema, so a schema name cannot be given when creating a temporary table
+        schema=models.BASE.metadata.schema if oracle_table_is_global else None,  # Temporary tables exist in a special schema, so a schema name cannot be given when creating a temporary table
         **additional_kwargs,
     )
 
@@ -451,7 +463,7 @@ def create_scope_name_table(self, logger=logging.log):
             Column("name", String(get_schema_value('NAME_LENGTH'))),
         ]
         return self.create_temp_table(
-            'temporary_scope_name',
+            'TEMPORARY_SCOPE_NAME',
             *columns,
             primary_key=columns,
             logger=logger,
@@ -469,7 +481,7 @@ def create_association_table(self, logger=logging.log):
             Column("child_name", String(get_schema_value('NAME_LENGTH'))),
         ]
         return self.create_temp_table(
-            'temporary_association',
+            'TEMPORARY_ASSOCIATION',
             *columns,
             primary_key=columns,
             logger=logger,
@@ -481,7 +493,7 @@ def create_id_table(self, logger=logging.log):
         """
 
         return self.create_temp_table(
-            'temporary_id',
+            'TEMPORARY_ID',
             Column("id", models.GUID()),
             logger=logger,
         )


Test patch: