pipeline_serializer.ex (1880B)
1 defmodule Absinthe.Subscription.PipelineSerializer do 2 @moduledoc """ 3 Serializer responsible for packing and unpacking pipeline stored in the Elixir registry. 4 5 The purpose of this logic is saving memory by deduplicating repeating options - (ETS 6 backed registry stores them flat in the memory). 7 """ 8 9 alias Absinthe.{Phase, Pipeline} 10 11 @type options_label :: {:options, non_neg_integer()} 12 13 @type packed_phase_config :: Phase.t() | {Phase.t(), options_label()} 14 15 @type options_map :: %{options_label() => Keyword.t()} 16 17 @type packed_pipeline :: {:packed, [packed_phase_config()], options_map()} 18 19 @spec pack(Pipeline.t()) :: packed_pipeline() 20 def pack(pipeline) do 21 {packed_pipeline, options_reverse_map} = 22 pipeline 23 |> List.flatten() 24 |> Enum.map_reduce(%{}, &maybe_pack_phase/2) 25 26 options_map = Map.new(options_reverse_map, fn {options, label} -> {label, options} end) 27 28 {:packed, packed_pipeline, options_map} 29 end 30 31 @spec unpack(Pipeline.t() | packed_pipeline()) :: Pipeline.t() 32 def unpack({:packed, pipeline, options_map}) do 33 Enum.map(pipeline, fn 34 {phase, {:options, _n} = options_label} -> 35 {phase, Map.fetch!(options_map, options_label)} 36 37 phase -> 38 phase 39 end) 40 end 41 42 def unpack([_ | _] = pipeline) do 43 pipeline 44 end 45 46 defp maybe_pack_phase({phase, options}, options_reverse_map) do 47 if Map.has_key?(options_reverse_map, options) do 48 options_label = options_reverse_map[options] 49 50 {{phase, options_label}, options_reverse_map} 51 else 52 new_index = map_size(options_reverse_map) 53 options_label = {:options, new_index} 54 options_reverse_map = Map.put(options_reverse_map, options, options_label) 55 56 {{phase, options_label}, options_reverse_map} 57 end 58 end 59 60 defp maybe_pack_phase(phase, options_reverse_map) do 61 {phase, options_reverse_map} 62 end 63 end