Slide 1

Slide 1 text

Keep Phoenix App Productivity ᇙ໌ल XFLAG ελδΦ

Slide 2

Slide 2 text

Who am I • Akihide Kan(@kanmo_ak) • XFLAG(mixi, Inc.) • ໿൒೥લʹݱࡏͷϓϩδΣΫτʢElixirͷήʔϜαʔό ։ൃʣʹࢀՃ

Slide 3

Slide 3 text

Elixir on XFLAG • Elixir͸ήʔϜͷଞʹID؅ཧɺܾࡁɺίϛϡχέʔγϣ ϯαʔϏεɺECαΠτͷҰ෦Ͱར༻͍ͯ͠Δ • ৽نࣄۀͰ΋ଟ͘࠾༻͞Ε͍ͯΔ • Elixir࢖͍ͬͯΔϝϯόʔ͸େମ40ਓ͘Β͍

Slide 4

Slide 4 text

Objectives • ։ൃ1೥ɺӡ༻1೥ଓ͚͖ͯͨதͰPhoenix Application ͷੜ࢈ੑΛԼ͛Δ໰୊ʹͲ͏ରॲ͖͔ͯͨ͠ڞ༗ • Umbrella • Compile • Monitoring

Slide 5

Slide 5 text

Our Project

Slide 6

Slide 6 text

Our project • ϞόΠϧήʔϜͷόοΫΤϯυ • Elixir1.5.1ɺ Phoenix1.3.0 • 2016/06։ൃελʔτɺ2017/06ӡ༻։࢝ • ։ൃ౰࣌͸Elixir1.1ɺPhoenix1.1

Slide 7

Slide 7 text

Architecture

Slide 8

Slide 8 text

։ൃ౰࣌ͷ໨తɺؔ৺ • ڠྗ/ରઓήʔϜɺଞϢʔβʔ΁ͷ௨஌ • αʔόʹෳࡶͳϩδοΫΛ࣋ͯΔ͔ • ΫϥΠΞϯτͱͷ઀ଓΛ੾ΒͣʹσϓϩΠͰ͖Δ͔ • ϥΠϒϥϦͷ਺ɺߋ৽ස౓

Slide 9

Slide 9 text

Elixir Conf 2017Ͱൃද

Slide 10

Slide 10 text

αʔϏε։࢝ ͦͷޙແࣄϦϦʔε ໿̍೥ؒɺػೳ։ൃΛଓ͚ͨ

Slide 11

Slide 11 text

Running for 1 year $ grep ‘defmodule’ apps/**/*.ex{,s} | wc -l 4083 $ wc -l apps/**/*.ex{,s} | tail -n1 326131 total

Slide 12

Slide 12 text

Phoenix Productivity http://phoenixframework.org/

Slide 13

Slide 13 text

Umbrella Project

Slide 14

Slide 14 text

Many Services • ࠷ॳ͸ҰͭͷPhoenixΞϓϦέʔγϣϯͱͯ͠ελʔτ • ΞϓϦέʔγϣϯ͕੒௕͢ΔʹͭΕͯଟ͘ͷαʔϏεΛ ։ൃ͢Δඞཁ͕ग़͖ͯͨ • ίϯςϯπ؅ཧɺCSɺӡ༻πʔϧɺetc • Deploy΋͕͔͔࣌ؒΔ

Slide 15

Slide 15 text

Umbrella Project

Slide 16

Slide 16 text

Umbrella Project

Slide 17

Slide 17 text

Umbrella Project Feature • ෳ਺ͷαʔϏεΛҰͭͷϦϙδτϦͰผʑʹ։ൃ͢Δ • αʔϏεؒͰίʔυΛڞ༗Ͱ͖Δ • αʔϏεຖʹσϓϩΠɺςετ • ࠷ॳ͸ϞϊϦεͱͯ͠։ൃΛਐΊɺେ͖͘ͳͬͨΒΞϓ ϦέʔγϣϯΛ෼཭͢Δ

Slide 18

Slide 18 text

Umbrella Deploy

Slide 19

Slide 19 text

Distillery Release # rel/config.exs release :test_app_all do set version: “0.0.1" set applications: [:test_app_api, :test_app_socket, :test_app_cs, :test_app_cms, :test_app_to ols] … end # release separately release :test_app_api do set version: current_version(:test_app_api) set applications: [:test_app_api] … end

Slide 20

Slide 20 text

Separate logic

Slide 21

Slide 21 text

Separate logic def deps do # api_app, mix.exs {:interface_lib_app, inumbrella: true} … end

Slide 22

Slide 22 text

Umbrella or Microservices Repository Interface Deploy Umbrella Single Via subapp Both(one app or separately) Microservices Separate Repositories Defined Interface Separately

Slide 23

Slide 23 text

Elixir Compile

Slide 24

Slide 24 text

Elixir Compile • Elixir͸macroʹΑͬͯmeta programmingͷػೳΛ࣮ ݱ • macro͸ίϯύΠϧ࣌ʹͷΈධՁ • Compile time dependencies͕ੜ·ΕΔͱͦͷϞδϡʔ ϧʹؔ࿈ͨ͠Ϟδϡʔϧ΋ίϯύΠϧର৅ʹͳΔ

Slide 25

Slide 25 text

Compile Time Dependencies defmodule MacroTest do require MacroA def test, do: MacroA.use_macro("hello") end defmodule MacroA do defmacro use_macro(x) do if ModuleA.func() do quote(do: unquote("#{x} macro")) else quote(do: unquote("#{x} dependency")) end end end

Slide 26

Slide 26 text

Compile Time Dependencies defmodule MacroTest do require MacroA def test, do: MacroA.use_macro("hello") end defmodule MacroA do defmacro use_macro(x) do if ModuleA.func() do quote(do: unquote("#{x} macro")) else quote(do: unquote("#{x} dependency")) end end end

Slide 27

Slide 27 text

Compile Time Dependencies defmodule StructTest do def test do %StructA{field: “hello”} end end defmodule StructA do defstruct [:field] end

Slide 28

Slide 28 text

Compile Time Dependencies defmodule StructTest do def test do %StructA{field: “hello”} end end defmodule StructA do defstruct [:field] end

Slide 29

Slide 29 text

Compile Time Dependencies defmodule Test do import ModuleA end defmodule BehaviourTest do @behaviour BehaviourA def test(x), do: x end

Slide 30

Slide 30 text

Compile Time Dependencies defprotocol ProtocolA do def test(x) end # protocol_impl.ex defimpl ProtocolA, for: StructA do def test(_), do: “test” end # protocol_caller.ex defmodule ProtocolCaller do def use_protocol(x), do ProtocolA.test(x) end end

Slide 31

Slide 31 text

Compile Time Dependencies defprotocol ProtocolA do def test(x) end # protocol_impl.ex defimpl ProtocolA, for: StructA do def test(_), do: “test” end # protocol_caller.ex defmodule ProtocolCaller do def use_protocol(x), do ProtocolA.test(x) end end

Slide 32

Slide 32 text

Mix xref $ mix xref graph —format dot

Slide 33

Slide 33 text

Mix xref $ mix xref graph —format dot digraph "xref graph" { "lib/macro_a.ex" "lib/macro_a.ex" -> “lib/module_a.ex" "lib/macro_test.ex" "lib/macro_test.ex" -> "lib/macro_a.ex" [label="(compile)"] }

Slide 34

Slide 34 text

Mix xref $ mix xref graph —format dot digraph "xref graph" { "lib/macro_a.ex" "lib/macro_a.ex" -> “lib/module_a.ex" "lib/macro_test.ex" "lib/macro_test.ex" -> "lib/macro_a.ex" [label="(compile)"] }

Slide 35

Slide 35 text

Dirty Trick defmodule MacroTest do def test() do func_map = %{key1: Test.Module.Function1, key2: Test.Module.Function2} end end

Slide 36

Slide 36 text

Dirty Trick defmodule MacroTest do def test() do func_map = %{key1: Module.concat([Test, Module, Function1], key2: Module.concat([Test, Module, Function2]} end end defmodule MacroTest do def test() do func_map = %{key1: Test.Module.Function1, key2: Test.Module.Function2} end end

Slide 37

Slide 37 text

Slow Test

Slide 38

Slide 38 text

Slow Test • Test࣮ߦ࣌ؒΑΓϩʔυɺίϯύΠϧ͕࣌ؒ஗͍ • 1ϑΝΠϧमਖ਼͚ͨͩ͠Ͱ΋଴ͨ͞ΕΔ • ຊདྷ͸ద੾ͳαΠζͰϓϩδΣΫτΛ෼ׂ͢Δͷ͕ྑ͍ ͕ɺͦ͏Ͱ͖ͳ͍ͱ͖͸IExͷϔϧύʔؔ਺Λ࡞ͬͨ

Slide 39

Slide 39 text

IEx Helper # .iex.exs defmodule IExHelper do def rerun_spec(file_name) do Mix.Task.reenable “espec” … Code.load_file(file_name) |> get_in([Access.at(0), Access.elem(0)]) |> ESpec.SuiteRunner.run(ESpec.Configuration.all) |> ESpec.Output.final_result() end end

Slide 40

Slide 40 text

Monitoring and Operation

Slide 41

Slide 41 text

Prometheus and Grafana ओʹ؂ࢹ͍ͯ͠Δ߲໨ ETS table, Output&Received Bytes, Context Switches, Run Queues Length, Processes

Slide 42

Slide 42 text

API Metrics

Slide 43

Slide 43 text

Example

Slide 44

Slide 44 text

Example

Slide 45

Slide 45 text

Prometheus and Grafana Prometheus ClientͷϥΠϒϥϦ΍Erlang VM༻ͷGrafana μογϡϘʔυΛར༻͍ͯ͠Δ

Slide 46

Slide 46 text

$=proc:<0.13622.1> State: Garbing Name: ‘Elixir.Timex_meck' Spawned as: proc_lib:init_p/5 Spawned by: <0.4265.0> Started: Wed May 17 05:15:18 2017 Message queue length: 4 Number of heap fragments: 0 Heap fragment data: 14666 Link list: [<0.4265.0>, {from,<0.16290.3>,#Ref<0.0.5505025.4620>}] Reductions: 79249321 Stack+heap: 164783480 … Remsh remshͰىಈதͷphoenixϓϩηεʹ઀ଓͯ͠ௐࠪͰ͖Δ

Slide 47

Slide 47 text

$=proc:<0.13622.1> State: Garbing Name: ‘Elixir.Timex_meck' Spawned as: proc_lib:init_p/5 Spawned by: <0.4265.0> Started: Wed May 17 05:15:18 2017 Message queue length: 4 Number of heap fragments: 0 Heap fragment data: 14666 Link list: [<0.4265.0>, {from,<0.16290.3>,#Ref<0.0.5505025.4620>}] Reductions: 79249321 Stack+heap: 164783480 … $ iex —name “app_name” —cookie “mycookie” —remsh “app_name-11111@hostname iex()1> Process.whereis(Elixir.Timex_meck) #PID<0.19553.2> iex()2>:erlang.process_info(pid(“0.19553.2”), :total_heap_s ize) {:total_heap_size, 224366925} Remsh remshͰىಈதͷphoenixϓϩηεʹ઀ଓͯ͠ௐࠪͰ͖Δ

Slide 48

Slide 48 text

Library

Slide 49

Slide 49 text

Library • total: 6647 (2018/06/13) • ඞཁͳ΋ͷ͸Ұ௨Γἧ͍ͬͯΔ

Slide 50

Slide 50 text

Library • ͳ͍΋ͷ͸࡞ͬͨɺ΋͘͠͸Ұ෦มߋͯ͠࢖ͬͯΔ • nats pubsub • db sharding • cache utility • redis cluster

Slide 51

Slide 51 text

Conclusion

Slide 52

Slide 52 text

·ͱΊ • ΞϓϦέʔγϣϯ͕େ͖͘ͳΔʹͭΕͯੜ࢈ੑ͕Լ͕Δ໰୊͕ग़ͯ ͘Δʢcompile, test, deploy…ʣ • Umbrella͸ϓϩδΣΫτ෼ׂΛ؆୯ʹͰ͖ΔΑ͏ʹαϙʔτ͠ ͯ͘ΕΔ • Compileର৅͕ଟ͍৔߹͸ɺແବͳCompile Time DependenciesΛ࡟আ͢Δ • Ϣʔβʔͷ઀ଓ؅ཧͱϩδοΫΛॲཧ͢ΔαʔόΛ෼͚ͨ

Slide 53

Slide 53 text

·ͱΊ • LibraryɺFWͳͲͷΤίγεςϜʹ͍ͭͯ • ։ൃɺӡ༻Λͯ͠ΈͯLibrary͕গͳ͗͢Δ໰୊͸ಛʹͳ͔ͬͨ • ඞཁͳ࣌ʹ࡞Ε͹ྑ͍(ύοέʔδϚωʔδϟʔɺπʔϧ͕༏ल) • ElixirɺPhoenixͷόʔδϣϯΞοϓ͸׆ൃ →ɹؤுͬͯ௥ै • (Type specΛॻ͍͓ͯ͘ͱυΩϡϝϯτʹͳΔͷͰίʔυ͕ಡΈ΍͢ ͍)

Slide 54

Slide 54 text

Thank you