diff --git a/api/src/org/labkey/api/data/MaterializedQueryHelper.java b/api/src/org/labkey/api/data/MaterializedQueryHelper.java index 226abff3031..7a4bae8472c 100644 --- a/api/src/org/labkey/api/data/MaterializedQueryHelper.java +++ b/api/src/org/labkey/api/data/MaterializedQueryHelper.java @@ -177,6 +177,7 @@ boolean load(SQLFragment selectQuery, boolean isSelectInto) { new SqlExecutor(_mqh._scope).execute(StringUtils.replace(index, "${NAME}", _tableName)); } + analyze(); } _loadingState.set(LoadingState.LOADED); @@ -194,6 +195,22 @@ boolean load(SQLFragment selectQuery, boolean isSelectInto) } } + /** SELECT INTO leaves the table with no statistics, so the planner guesses until autovacuum eventually analyzes it. */ + private void analyze() + { + try + { + SQLFragment sql = _mqh._scope.getSqlDialect().getAnalyzeCommandForTable(_fromSql); + if (null != sql) + new SqlExecutor(_mqh._scope).execute(sql); + } + catch (RuntimeException x) + { + // Statistics are an optimization, so a dialect that can't analyze must not fail the materialization + LOG.warn("Failed to update statistics for materialized table {}", _tableName, x); + } + } + public void setError(RuntimeException rex) { _loadException = rex; diff --git a/experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java b/experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java index 38f71e66222..2a7d409fe3b 100644 --- a/experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java @@ -1260,7 +1260,8 @@ void recordPendingUpdate(@NotNull Timestamp changedSince) } } - static final BlockingCache _materializedQueries = CacheManager.getBlockingStringKeyCache(CacheManager.UNLIMITED, CacheManager.HOUR, "materialized sample types", null); + // TTL is a backstop for missed invalidations. Admins can manually clear via Admin Console->Caches + static final BlockingCache _materializedQueries = CacheManager.getBlockingStringKeyCache(CacheManager.UNLIMITED, 12 * CacheManager.HOUR, "materialized sample types", null); static final Map _invalidationCounters = Collections.synchronizedMap(new HashMap<>()); static final AtomicBoolean initializedListeners = new AtomicBoolean(false);