Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow disabling cache in OpenApiSpex.Plug.PutApiSpec #72

Closed
hauleth opened this issue Jan 2, 2019 · 9 comments
Closed

Allow disabling cache in OpenApiSpex.Plug.PutApiSpec #72

hauleth opened this issue Jan 2, 2019 · 9 comments

Comments

@hauleth
Copy link
Contributor

hauleth commented Jan 2, 2019

Caching can be problematic in development and cleaning configuration after each recompilation can be troublesome.

Additionally such behaviour can be problematic when doing hot upgrades.

One possible solution is to store mod.module_info(:md5) along the cached data and drop everything on hash change.

hauleth added a commit to hauleth/open_api_spex that referenced this issue Jan 3, 2019
This allows code to be automatically reloaded on code update enabling
hot code reload in development and in releases.

Close open-api-spex#72
hauleth added a commit to hauleth/open_api_spex that referenced this issue Feb 6, 2019
In Phoenix applications this should not have any impact on performance
when used with `:plug_init_mode` set to `:compile` (default) while
provide recompilation of schema in development. Additionally this will
allow using hot-reloads.

Close open-api-spex#72
@hauleth
Copy link
Contributor Author

hauleth commented Mar 25, 2019

My current approach to that is to use persistent_term (OTP 21.2+ only) and @on_load callback in my controllers:

defmodule KokonRest.SpecCache do
  @moduledoc false

  @type operation_lookup :: %{optional(String.t) => OpenApiSpex.Operation.t()}
  @type spec :: %{
    spec: OpenApiSpex.OpenApi.t(),
    operation_lookup: operation_lookup()
  }

  @spec get_spec(module()) :: spec()
  def get_spec(mod) do
    case :persistent_term.get(__MODULE__, %{}) do
      %{^mod => spec} ->
        spec

      map ->
        spec = build_lookup(mod)

        :persistent_term.put(__MODULE__, Map.put(map, mod, spec))

        spec
    end
  end

  @spec clean() :: :ok
  def clean do
    if :persistent_term.erase(__MODULE__),
      do: :logger.info("Clearing spec cache")

    :ok
  end

  @spec build_lookup(module()) :: spec()
  defp build_lookup(mod) do
    spec = mod.spec()

    operation_lookup =
      for {_name, item} <- spec.paths,
          %OpenApiSpex.Operation{} = operation <- Map.values(item),
          into: %{},
          do: {operation.operationId, operation}

    %{spec: spec, operation_lookup: operation_lookup}
  end
end

And in my API controllers I have:

@on_load :__clean_cache__

def __clean_cache__, do: KokonRest.SpecCache.clean()

And it seems to work right now, even with Phoenix.CodeReload.

@dmt
Copy link
Contributor

dmt commented Apr 1, 2019

👍

I'm finding development pretty challenging at the moment. It seems essentially impossible to change a schema without also renaming it.

Maybe I'm missing something. What is the benefit of persisting this between restarts? Could this at least be a configuration option?

@hauleth
Copy link
Contributor Author

hauleth commented Apr 1, 2019

@dmt this do not persist schema between restarts. This caches the resulting schema to not recalculate it between each request (pretty handy), but on the other hand it prevents any hot-reloads and live development (especially when used together with OpenApiSpex.Plug.CastAndValidate. My current approach, that can be seen in comment above, is to use persistent_term and clean it on each module reload. Alternative to that could be launching ETS table to cache builds that would be cleaned on given modules reload (like shown).

@dmt
Copy link
Contributor

dmt commented Apr 1, 2019

Thanks. I see now that my issues must be coming from somewhere else. I'll need to investigate more when I have the time.

@hauleth
Copy link
Contributor Author

hauleth commented Apr 1, 2019

Another idea I have is to check if file (for example) priv/schema.json (format could be different, for example we could use ETF) and if it is present, then compile function directly, if not present, then generate schema on the fly. But to be honest the ETS/persistent_term seems simpler.

@moxley
Copy link
Contributor

moxley commented Apr 4, 2019

@hauleth

My current approach to that is to use persistent_term (OTP 21.2+ only)

I'm not sure you're aware, but we recently changed PutApiSpec to use persistent_term when it's available.

https://github.com/open-api-spex/open_api_spex/blob/master/lib/open_api_spex/plug/put_api_spec.ex#L53-L65

@hauleth
Copy link
Contributor Author

hauleth commented Apr 4, 2019

@moxley it still do not saves the problem (oh and by the way, you do not need whole separate module, just if would be enough), it just changes the engine that will be used for caching.

@moxley
Copy link
Contributor

moxley commented Apr 4, 2019

@hauleth

it still do not saves the problem

Right, I'm aware of that. But you may want to merge that with your solution, since there is duplication involved.

@mbuhot
Copy link
Collaborator

mbuhot commented Aug 22, 2020

Should be resolved by #262 in version 3.8.0

@mbuhot mbuhot closed this as completed Aug 22, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants