LibrePortal/scripts/source/loading/initilize_files.sh
librelad c68254ad70 feat(lazy-load): dual loader with LP_LAZY=1 opt-in (Phase 3)
scripts/source/loading/initilize_files.sh gains an LP_LAZY=1 branch:
  - Sources scripts/source/files/arrays/function_manifest.sh once. The
    manifest defines LP_FN_MAP, LP_EAGER_FILES, AND ~700 autoload stubs
    (precompiled by the generator — one parse cost vs evaluating 700
    snippets at startup).
  - Eager-sources every file listed in LP_EAGER_FILES (top-level side
    effects: variable assignments, source calls, bare commands). These
    can't safely be deferred — they'd skip the side effect, not just the
    function definition.
  - Skips the bulk loop that sources every files_to_source[@] entry.

Default behaviour (LP_LAZY unset or 0) is byte-identical to the previous
loader — every file gets eager-sourced up front. Long-running processes
(WebUI service, task processor) leave LP_LAZY unset because their first
call to anything wants the function already hot.

Each autoload stub looks like:
  funcname() {
    source "${install_scripts_dir}path/to/file.sh"
    funcname "$@"
  }

First call sources the real file, which redefines the function with the
real body; the stub's trailing `funcname "$@"` then calls the freshly-
defined real implementation. Sourcing the file also redefines stubs for
any sibling functions the same file declares, so they don't re-source.

Safety nets:
- Missing manifest → fall back to eager loading (`export LP_LAZY=0`).
  No regression risk if someone enables LP_LAZY=1 on a stale install
  whose regen never ran.
- LP_LOAD_TRACE=1 still works in lazy mode — it records the manifest
  parse + each eager file (tagged LAZY-manifest / LAZY-EAGER) so Phase 4
  can measure the actual saving.

No automatic flip yet — this commit only adds the path. Phase 4 will set
LP_LAZY=1 by default for the CLI entrypoint (and re-measure with the
trace tool from Phase 1).

Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-26 20:51:24 +01:00

123 lines
5.3 KiB
Bash
Executable File

#!/bin/bash
sourceInitilize()
{
local flag="$1"
# We will only show the header for the full app
if [[ $flag == "run" ]]; then
isHeader "Loading LibrePortal Startup Files"
isNotice "If you are experiencing loading issues..."
isNotice "Please run the following : 'libreportal reset'"
fi
# Directory containing the files to source recursively
local file_list_directory="${install_scripts_dir}source/files"
# Check if the directory exists
if [ -d "$file_list_directory" ]; then
# Use find to get a list of all files (excluding directories) in the directory and its subdirectories
local file_list=$(find "$file_list_directory" -type f -name "*.sh")
# Loop through each file in the file list
while IFS= read -r file; do
# Source the file
source "$file"
done <<< "$file_list"
else
echo "Directory $file_list_directory does not exist. Unable to start!"
fi
# For loading files needed for the full app or CLI
if [[ $flag == "run" ]]; then
source "${install_scripts_dir}source/files/app_files.sh"
files_to_source=("${files_libreportal_app[@]}")
elif [[ $flag == "cli" ]]; then
source "${install_scripts_dir}source/files/cli_files.sh"
files_to_source=("${files_libreportal_cli[@]}")
fi
# Trace setup — LP_LOAD_TRACE=1 logs `<ms>\t<file>` to LP_LOAD_TRACE_FILE
# for `libreportal debug load-trace`. Zero overhead when unset.
if [[ "$LP_LOAD_TRACE" == "1" ]]; then
: "${LP_LOAD_TRACE_FILE:=/tmp/libreportal-load-trace.$$.log}"
export LP_LOAD_TRACE_FILE
: > "$LP_LOAD_TRACE_FILE"
fi
# LP_LAZY=1: defer the bulk of file sourcing. Install autoload stubs from
# function_manifest.sh, source only the files with top-level side effects
# (LP_EAGER_FILES — set by the manifest), and skip the rest. Each stub
# sources its real file on first call; subsequent calls hit the real
# function directly (the source replaced the stub).
#
# Default (LP_LAZY unset or 0): the historical behaviour — source every
# file in files_to_source up front. Long-running processes (task
# processor, WebUI) want this so their first call to anything is hot.
if [[ "$LP_LAZY" == "1" ]]; then
local manifest="${install_scripts_dir}source/files/arrays/function_manifest.sh"
if [[ -f "$manifest" ]]; then
# Sourcing the manifest defines LP_FN_MAP, LP_EAGER_FILES, AND
# the autoload stubs (precompiled by the generator so we pay one
# parse cost instead of evaling ~700 snippets at startup).
if [[ "$LP_LOAD_TRACE" == "1" ]]; then
local _t0=$EPOCHREALTIME
source "$manifest"
local _t1=$EPOCHREALTIME
local _ms
_ms=$(awk -v a="$_t0" -v b="$_t1" 'BEGIN{printf "%.3f", (b-a)*1000}')
printf '%s\t%s\n' "$_ms" "source/files/arrays/function_manifest.sh (LAZY-manifest)" >> "$LP_LOAD_TRACE_FILE"
else
source "$manifest"
fi
# Eager-source the side-effect files. These define vars or run
# commands at top level; lazy stubs would skip those side effects.
local _eager
for _eager in "${LP_EAGER_FILES[@]}"; do
[[ -f "${install_scripts_dir}${_eager}" ]] || continue
if [[ "$LP_LOAD_TRACE" == "1" ]]; then
local _t0=$EPOCHREALTIME
source "${install_scripts_dir}${_eager}"
local _t1=$EPOCHREALTIME
local _ms
_ms=$(awk -v a="$_t0" -v b="$_t1" 'BEGIN{printf "%.3f", (b-a)*1000}')
printf '%s\t%s\n' "$_ms" "${_eager} (LAZY-EAGER)" >> "$LP_LOAD_TRACE_FILE"
else
source "${install_scripts_dir}${_eager}"
fi
done
else
# No manifest present — fall back to eager loading so we never
# leave the user with a broken install just because regen
# hasn't run. Silently — this is the safety net, not the path.
export LP_LAZY=0
fi
fi
# Eager path (default OR lazy-with-no-manifest fallback). Sources every
# file in files_to_source. Skipped entirely when LP_LAZY=1 succeeded.
if [[ "$LP_LAZY" != "1" ]]; then
for file_to_source in "${files_to_source[@]}"; do
if [ ! -f "${install_scripts_dir}${file_to_source}" ]; then
isNotice "Missing file: ${install_scripts_dir}${file_to_source}"
else
if [[ "$LP_LOAD_TRACE" == "1" ]]; then
local _t0=$EPOCHREALTIME
source "${install_scripts_dir}${file_to_source}"
local _t1=$EPOCHREALTIME
local _ms
_ms=$(awk -v a="$_t0" -v b="$_t1" 'BEGIN{printf "%.3f", (b-a)*1000}')
printf '%s\t%s\n' "$_ms" "$file_to_source" >> "$LP_LOAD_TRACE_FILE"
else
source "${install_scripts_dir}${file_to_source}"
fi
fi
done
fi
# Loading of all files
sourceScanFiles "libreportal_configs";
sourceScanFiles "app_configs";
sourceScanFiles "containers";
}