diff --git a/src/nova_basic_handler.erl b/src/nova_basic_handler.erl index 05669b2..5794295 100644 --- a/src/nova_basic_handler.erl +++ b/src/nova_basic_handler.erl @@ -304,18 +304,24 @@ handle_view(View, Variables, Options, Req) -> {ok, Req2}. render_dtl(View, Variables, Options) -> - case code:is_loaded(View) of - false -> - case code:load_file(View) of - {error, Reason} -> - %% Cast a warning since the module could not be found - ?LOG_ERROR(#{msg => <<"Nova could not render template">>, template => View, reason => Reason}), + %% Erlang's code server will auto-load modules on first call + %% Try to render and catch cases where module or function doesn't exist + try View:render(Variables, Options) of + Result -> Result + catch + error:undef:Stacktrace -> + %% Check if the error is specifically from View:render/2 + %% (arity 2 is the standard erlydtl render function signature) + case Stacktrace of + [{View, render, 2, _} | _] -> + %% Module doesn't exist or render/2 not exported + ?LOG_ERROR(#{msg => <<"Nova could not render template">>, + template => View, reason => module_not_found}), throw({404, {template_not_found, View}}); _ -> - View:render(Variables, Options) - end; - _ -> - View:render(Variables, Options) + %% undef error from somewhere else, re-raise it + erlang:raise(error, undef, Stacktrace) + end end. diff --git a/src/nova_router.erl b/src/nova_router.erl index 414e8b7..b95f5db 100644 --- a/src/nova_router.erl +++ b/src/nova_router.erl @@ -72,6 +72,7 @@ compile(Apps) -> when Req::cowboy_req:req(), Env0::cowboy_middleware:env(). execute(Req = #{host := Host, path := Path, method := Method}, Env) -> + %% Cache the storage backend lookup to avoid repeated calls StorageBackend = application:get_env(nova, dispatch_backend, persistent_term), Dispatch = StorageBackend:get(nova_dispatch), case routing_tree:lookup(Host, Path, Method, Dispatch) of @@ -133,11 +134,9 @@ lookup_url(Host, Path) -> lookup_url(Host, Path, '_'). lookup_url(Host, Path, Method) -> + %% Fetch both values together to avoid duplicate application:get_env calls StorageBackend = application:get_env(nova, dispatch_backend, persistent_term), Dispatch = StorageBackend:get(nova_dispatch), - lookup_url(Host, Path, Method, Dispatch). - -lookup_url(Host, Path, Method, Dispatch) -> routing_tree:lookup(Host, Path, Method, Dispatch). %%-------------------------------------------------------------------- @@ -151,6 +150,7 @@ lookup_url(Host, Path, Method, Dispatch) -> add_routes(_App, []) -> ok; add_routes(App, [Routes|Tl]) when is_list(Routes) -> Options = #{}, + %% Cache the storage backend to avoid repeated application:get_env calls StorageBackend = application:get_env(nova, dispatch_backend, persistent_term), Dispatch = StorageBackend:get(nova_dispatch), @@ -184,8 +184,10 @@ add_routes(App, Routes) -> get_routes(Router, Env) -> %% Call the router Controllers = apply_callback(Router, controllers, [Env]), - apply_callback(Router, routes, [Env]) - ++ lists:append([nova_controller:routes(C, Env) || C <- Controllers ]). + RouterRoutes = apply_callback(Router, routes, [Env]), + ControllerRoutes = [nova_controller:routes(C, Env) || C <- Controllers], + %% Concatenate all route lists into one using a single list operation + lists:append([RouterRoutes | ControllerRoutes]). %% yields an empty list if callback does not exist apply_callback(Module, Function, Args) -> @@ -225,7 +227,9 @@ compile([App|Tl], Dispatch, Options) -> CompileParameters = Router:module_info(compile), RouterFile = proplists:get_value(source, CompileParameters), - Options1 = Options#{app => App, router_file => RouterFile}, + %% Cache global plugins in Options to avoid repeated lookups in compile_paths + GlobalPlugins = application:get_env(nova, plugins, []), + Options1 = Options#{app => App, router_file => RouterFile, global_plugins => GlobalPlugins}, {ok, Dispatch1, Options2} = compile_paths(Routes, Dispatch, Options1), @@ -244,8 +248,8 @@ compile_paths([], Dispatch, Options) -> {ok, Dispatch, Options}; compile_paths([RouteInfo|Tl], Dispatch, Options) -> App = maps:get(app, Options), RouterFile = maps:get(router_file, Options), - %% Fetch the global plugins - GlobalPlugins = application:get_env(nova, plugins, []), + %% Use cached global plugins from Options instead of repeated lookups + GlobalPlugins = maps:get(global_plugins, Options, []), Plugins = maps:get(plugins, RouteInfo, GlobalPlugins), Secure = @@ -396,6 +400,7 @@ render_status_page(StatusCode, Req) -> -spec render_status_page(StatusCode :: integer(), Data :: map(), Req :: cowboy_req:req()) -> {ok, Req0 :: cowboy_req:req(), Env :: map()}. render_status_page(StatusCode, Data, Req) -> + %% Fetch backend and dispatch together StorageBackend = application:get_env(nova, dispatch_backend, persistent_term), Dispatch = StorageBackend:get(nova_dispatch), render_status_page('_', StatusCode, Data, Req, #{dispatch => Dispatch}). @@ -406,6 +411,7 @@ render_status_page(StatusCode, Data, Req) -> Req :: cowboy_req:req(), Env :: map()) -> {ok, Req0 :: cowboy_req:req(), Env :: map()}. render_status_page(Host, StatusCode, Data, Req, Env) -> + %% Cache storage backend lookup StorageBackend = application:get_env(nova, dispatch_backend, persistent_term), Dispatch = StorageBackend:get(nova_dispatch), {Req0, Env0} = @@ -446,6 +452,7 @@ insert(Host, Path, Combinator, Value, Tree) -> add_plugin(Plugin) -> + %% Cache storage backend to avoid repeated lookups StorageBackend = application:get_env(nova, dispatch_backend, persistent_term), StoredPlugins = StorageBackend:get(?NOVA_PLUGINS, []), Plugins1 = lists:umerge([[Plugin], StoredPlugins]), diff --git a/src/nova_session.erl b/src/nova_session.erl index 6f4a704..4855ce4 100644 --- a/src/nova_session.erl +++ b/src/nova_session.erl @@ -114,7 +114,8 @@ delete(Req, Key) -> %%% Private functions %%%=================================================================== get_session_module() -> - application:get_env(nova, session_manager, nova_session_ets). + %% Use cached value from persistent_term for better performance + persistent_term:get(nova_session_manager, nova_session_ets). get_session_id(Req) -> case nova:get_env(use_sessions, true) of diff --git a/src/nova_sup.erl b/src/nova_sup.erl index adbc974..14b0797 100644 --- a/src/nova_sup.erl +++ b/src/nova_sup.erl @@ -65,6 +65,8 @@ init([]) -> Configuration = application:get_env(nova, cowboy_configuration, #{}), SessionManager = application:get_env(nova, session_manager, nova_session_ets), + %% Cache session manager in persistent_term for faster lookups in hot path + persistent_term:put(nova_session_manager, SessionManager), Children0 = [ child(nova_handlers, nova_handlers),