From 6eb413e76d01bb8dcd48a2a809980a1a59b16e9f Mon Sep 17 00:00:00 2001
From: Albert ARIBAUD <albert.aribaud@f-g.fr>
Date: Fri, 2 Aug 2024 13:40:00 +0200
Subject: [PATCH] Fix project_hierarchy_cache() to cache both full and
 enabled-only hierarchies

The way project_hierarchy_cache() works is:

    it caches the hierarchy of either all projects or only the enabled projects, based on the value of $p_show_disabled.
    consecutive calls with the same value for $p_show_disabled work as expected, the first one building the cache, the subsequent ones reusing it.
    consecutive calls with different values for $p_show_disabled induce a cache rebuild.

The way project_hierarchy_cache() is used in the project edit page is : for each sub-project, one call for with $p_show_disabled false, and one with $p_show_disabled true.

This negates caching entirely.

This patch manages two caches, one for the full hierarchy, and one for enabled
projects only.

With this patch, the edit page load page for a project with about 1700 sub-
projects goes down from about 20 seconds to about 350 ms.

diff --git a/core/project_hierarchy_api.php b/core/project_hierarchy_api.php
index 2d1161794..70a4431b4 100644
--- a/core/project_hierarchy_api.php
+++ b/core/project_hierarchy_api.php
@@ -31,9 +31,15 @@
 require_api( 'constant_inc.php' );
 require_api( 'database_api.php' );
 
+# We may need to build caches for either all projects or enabled projects or both
+$g_cache_project_hierarchy_enabled = null;
+$g_cache_project_hierarchy_all = null;
+$g_cache_project_inheritance_enabled = null;
+$g_cache_show_disabled_all = null;
+# To make it simpler for callers of project_hierarchy_cache(), we provide
+# these two aliases for the correct pair of caches above
 $g_cache_project_hierarchy = null;
 $g_cache_project_inheritance = null;
-$g_cache_show_disabled = null;
 
 /**
  * Add project to project hierarchy
@@ -146,14 +152,27 @@ function project_hierarchy_get_parent( $p_project_id, $p_show_disabled = false )
  * @return void
  */
 function project_hierarchy_cache( $p_show_disabled = false ) {
+	global $g_cache_project_hierarchy_enabled, $g_cache_project_inheritance_enabled;
+	global $g_cache_project_hierarchy_all, $g_cache_project_inheritance_all;
 	global $g_cache_project_hierarchy, $g_cache_project_inheritance;
-	global $g_cache_show_disabled;
 
-	if( !is_null( $g_cache_project_hierarchy ) && ( $g_cache_show_disabled == $p_show_disabled ) ) {
+	/* Should disabled projects be shown and do we already cache the whole hierarchy? */
+	if( ( !is_null( $p_show_disabled ) ) && !is_null( $g_cache_project_hierarchy_all ) ) {
+		/* Set aliases and return */
+		$g_cache_project_hierarchy = $g_cache_project_hierarchy_all;
+		$g_cache_project_inheritance = $g_cache_project_inheritance_all;
 		return;
 	}
-	$g_cache_show_disabled = $p_show_disabled;
 
+	/* Should disabled projects be hidden and do we already cache the enabled hierarchy? */ 
+	if( ( is_null( $p_show_disabled ) ) && !is_null( $g_cache_project_hierarchy_enabled ) ) {
+		/* Set aliases and return */
+		$g_cache_project_hierarchy = $g_cache_project_hierarchy_enabled;
+		$g_cache_project_inheritance = $g_cache_project_inheritance_enabled;
+		return;
+	}
+
+	/* Build the missing cache */
 	db_param_push();
 	$t_enabled_clause = $p_show_disabled ? '1=1' : 'p.enabled = ' . db_param();
 
@@ -187,6 +206,15 @@ function project_hierarchy_cache( $p_show_disabled = false ) {
 			$g_cache_project_inheritance[$t_project_id][$t_parent_id] = $t_parent_id;
 		}
 	}
+
+	/* Copy aliases into the right pair of caches */
+	if( is_null( $p_show_disabled ) ) {
+		$g_cache_project_hierarchy_enabled = $g_cache_project_hierarchy;
+		$g_cache_project_inheritance_enabled = $g_cache_project_inheritance;
+	} else {
+		$g_cache_project_hierarchy_all = $g_cache_project_hierarchy;
+		$g_cache_project_inheritance_all = $g_cache_project_inheritance;
+	}
 }
 
 /**
-- 
2.39.2

