├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── config └── config.exs ├── ea ├── SimplePool.EAP └── SimplePool.ldb ├── lib └── simple_pool │ ├── common │ ├── monitoring_framework │ │ ├── life_cycle_event.ex │ │ ├── server │ │ │ ├── health_check.ex │ │ │ └── resources.ex │ │ └── service │ │ │ └── health_check.ex │ └── telemetry.ex │ ├── nmid_adapter.ex │ ├── v1 │ ├── behaviour.ex │ ├── monitoring_framework │ │ ├── environment_manager │ │ │ ├── job_manager │ │ │ │ ├── job_manager_pool.ex │ │ │ │ └── job_manager_worker.ex │ │ │ ├── pool.ex │ │ │ └── worker_entity.ex │ │ ├── monitor_behaviour.ex │ │ ├── monitor_behaviour │ │ │ └── default.ex │ │ └── service │ │ │ └── definition.ex │ ├── pool_supervisor_behaviour.ex │ ├── server │ │ ├── environment_details.ex │ │ ├── provider_behaviour.ex │ │ ├── provider_behaviour │ │ │ └── default.ex │ │ └── state.ex │ ├── server_behaviour.ex │ ├── server_behaviour_default.ex │ ├── support │ │ ├── database.ex │ │ ├── dispatch_entity.ex │ │ ├── dispatch_monitor_repo.ex │ │ ├── dispatch_monitor_repo_behaviour.ex │ │ ├── dispatch_repo.ex │ │ ├── dispatch_repo_behaviour.ex │ │ ├── dispatch_repo_behaviour_default.ex │ │ ├── schema │ │ │ └── core.ex │ │ ├── schema_provider.ex │ │ └── topology_provider.ex │ ├── worker │ │ ├── health_check.ex │ │ ├── inner_state_behaviour.ex │ │ ├── link.ex │ │ └── state.ex │ ├── worker_behaviour.ex │ ├── worker_behaviour_default.ex │ ├── worker_lookup_behaviour │ │ ├── default.ex │ │ ├── default_implementation.ex │ │ ├── dynamic.ex │ │ ├── worker_lookup_behaviour.ex │ │ └── worker_lookup_behaviour_default.ex │ └── worker_supervisor_behaviour.ex │ └── v2 │ ├── behaviours │ ├── message_processing_behaviour.ex │ ├── router │ │ └── router_provider.ex │ ├── router_behaviour.ex │ ├── service_management │ │ └── service_management_provider.ex │ ├── service_management_behaviour.ex │ ├── settings_behaviour.ex │ ├── worker_management │ │ └── worker_management_provider.ex │ └── worker_management_behaviour.ex │ ├── cluster_management_framework │ ├── provider │ │ ├── cluster_manager.ex │ │ ├── node │ │ │ └── node_manager.ex │ │ └── service │ │ │ └── service_manager.ex │ └── structures │ │ ├── entities │ │ ├── cluster │ │ │ ├── definition.ex │ │ │ ├── node │ │ │ │ ├── definition.ex │ │ │ │ ├── state_entity.ex │ │ │ │ └── status.ex │ │ │ ├── service │ │ │ │ ├── definition.ex │ │ │ │ ├── instance │ │ │ │ │ ├── definition.ex │ │ │ │ │ ├── state_entity.ex │ │ │ │ │ └── status.ex │ │ │ │ ├── state_entity.ex │ │ │ │ └── status.ex │ │ │ ├── state_entity.ex │ │ │ └── status.ex │ │ ├── health_report.ex │ │ ├── health_report │ │ │ └── check.ex │ │ └── health_report_definition.ex │ │ └── repos │ │ └── cluster │ │ ├── node │ │ └── state_repo.ex │ │ ├── service │ │ └── state_repo.ex │ │ └── state_repo.ex │ ├── dispatch_entity.ex │ ├── monitor_behaviour.ex │ ├── pool_behaviour.ex │ ├── pool_supervisor_behaviour.ex │ ├── record_keeper_behaviour.ex │ ├── server │ └── state.ex │ ├── server_behaviour.ex │ ├── stand_alone_service_behaviour.ex │ ├── support │ ├── database.ex │ ├── schema │ │ └── core.ex │ ├── schema_provider.ex │ └── topology_provider.ex │ ├── worker │ └── inner_state_behaviour.ex │ ├── worker_behaviour.ex │ ├── worker_supervisor │ └── layer2_behaviour.ex │ └── worker_supervisor_behaviour.ex ├── mix.exs ├── mix.lock ├── run-test.sh ├── test-node.sh └── test ├── README.md ├── simple_pool ├── v1 │ ├── basic_test.exs │ ├── environment_manager_test.exs │ └── event_entry_tests.exs.disabled └── v2 │ ├── acceptance_test.exs │ └── routing_test.exs ├── support ├── erp.ex ├── nmid_generator.ex ├── test_database.ex ├── test_helpers.ex ├── test_wait.ex ├── v1 │ └── services │ │ ├── test_pool │ │ ├── test_pool.ex │ │ └── test_worker_entity.ex │ │ ├── test_three_pool │ │ ├── test_three_pool.ex │ │ └── test_three_worker_entity.ex │ │ └── test_two_pool │ │ ├── test_two_pool.ex │ │ └── test_two_worker_entity.ex └── v2 │ └── services │ ├── test_pool │ ├── test_pool.ex │ └── test_worker_entity.ex │ ├── test_three_pool │ ├── test_three_pool.ex │ └── test_three_worker_entity.ex │ └── test_two_pool │ ├── test_two_pool.ex │ └── test_two_worker_entity.ex └── test_helper.exs /.gitignore: -------------------------------------------------------------------------------- 1 | noizu_simple_pool.iml 2 | .idea 3 | .iml 4 | /_build 5 | /cover 6 | /deps 7 | erl_crash.dump 8 | *.ez 9 | Mnesia.first@127.0.0.1/ 10 | Mnesia.second@127.0.0.1/ 11 | xref_graph.dot 12 | xref_graph.png 13 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "doc"] 2 | path = doc 3 | url = git@github.com:noizu/SimplePool.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | # The MIT License 2 | 3 | Copyright (c) 2017 Noizu Labs, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SimplePool 2 | ================ 3 | 4 | **SimplePool** provides scaffolding for working with long lived distributed worker processes. 5 | SimplePool library has been highly optimized to work at scale and is capable of managing millions 6 | of frequently accessed long lived processes, while exposing the ability for developers to override 7 | internal behavior as needed by their individual projects or for specific worker types. 8 | 9 | **SimplePool** provides support for 10 | - monitoring services (failures per minute, etc.) 11 | - distributing load across a cluster. 12 | - counting workers across the distributed cluster. 13 | - checking process (alive/dead) across the distributed cluster. 14 | - rapid worker to node/process resolution. 15 | - enhanced message passing and processing including support for rerouting during worker migration or respawn. 16 | - lazy, asynchronous or sychronous pool worker spawning. 17 | - support for rebalancing workers across cluster (with per node target support) 18 | - support for offloading (deprovisioning) service(s) on a specific node for maintenance/upgrades. 19 | - automatic inactive process offloading. 20 | - target level based load balancing of new workers across cluster. 21 | 22 | What is it for? 23 | ---------------------------- 24 | Representing NPCs, Users, ChatRooms, IOT Devices where individual workers need to frequently respond to requests or perform background heart beat tasks. 25 | (Additional Documentation Pending) 26 | 27 | 28 | Key Components and Terms 29 | ---------------------------- 30 | (Additional Documentation Pending) 31 | 32 | ### Pool 33 | A pool of long lived processes. 34 | 35 | ### Pool.Worker 36 | Wrapper around user provided entities that implement the InnerStateBehaviour. Manages automatic deprovisioning, initilization handling, message routing, other maintenance tasks. 37 | 38 | ### Pool.Server 39 | Responsible for forwarding requests to underling messages. To avoid cpu bottle necks worker spawning and pid lookup along with most calls 40 | are performed within calling thread (or off process spawn). 41 | 42 | ### Pool.PoolSupervisor 43 | Top node in pool's OTP tree. 44 | 45 | ### Pool.WorkerSupervisor 46 | Manages Layer2 Worker Supervisor Segments 47 | 48 | ### Pool.WorkerSupervisor.Seg\[0-n\] 49 | Layer 2 of the Pool.WorkerSupervisor node. Per process compile time settings and run time options control how many supervisors are spawned. 50 | The large number of Segments coupled with fragmented Dispatch workers help to avoid per supervisor bottlenecks when rapidly spawning hundreds of thousands of workers on initial start. 51 | 52 | 53 | Version 2 54 | ---------------------------- 55 | The SimplePool Library includes two alternative implementations. 56 | To allow gradual migration of Pools from the previous implementation to the new implementation on existing projects. 57 | 58 | Version2 libraries take advantage of newer Elixir features and seeks to reduce some of the compile time overhead introduced 59 | by the forced recompiles caused from heavy reliance of interconnected modules and metaprogramming. 60 | Version2 additionally relies on ElixirScaffolding.V2 entity/repo types with the same considerations in mind. 61 | 62 | 63 | How it Works 64 | ---------------------------- 65 | (Additional Documentation Pending) 66 | 67 | What Happens Behind The Scenes 68 | ---------------------------- 69 | (Additional Documentation Pending) 70 | 71 | 72 | Example 73 | ---------------------------- 74 | See test/support/v2/services/test_pool for an example of a simple server/worker setup. 75 | 76 | ``` 77 | # Code Snippets Pending 78 | ``` 79 | 80 | Running Test Suite 81 | ---------------------------- 82 | Because SimplePool coordinates activites between multiple nodes the test suite requires some additional steps to run. 83 | To run the suite first launch the second node using `./test-node.sh` then in a second console window launch the actual suite with `./run-test.sh`. 84 | 85 | 86 | Related Technology 87 | --------------------------- 88 | - [ElixirScaffolding](https://github.com/noizu/ElixirScaffolding) - This library provides scaffolding for working with DomainObjects and Repos, access control, and including unique identifer ref tuples and protocols for converting between domain objects and ref tuples or vice versa. 89 | - [ElixirCore](https://github.com/noizu/ElixirCore) - Support for tracking call context between layers. Entity Reference Protocols, OptionHelper support for compile time options with defaults/and required field type and presence validation. 90 | - [MnesiaVersioning](https://github.com/noizu/MnesiaVersioning) - Simple Change Management Library for Mnesia/Amnesia. 91 | - [KitchenSink](https://github.com/noizu/KitchenSink) - Various useful libraries and tools that integrate with SimplePool and ElixirScaffolding. (CMS, Transactional Emails, SmartTokens, ...) 92 | - [RuleEngine](https://github.com/noizu/RuleEngine) - Extensible DB Driven Scripting/RuleEngine Library. 93 | 94 | 95 | 96 | Additional Documentation 97 | ---------------------------- 98 | * [Api Documentation](http://noizu.github.io/SimplePool) 99 | -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | # This file is responsible for configuring your application 7 | # and its dependencies with the aid of the Mix.Config module. 8 | # 9 | # This configuration file is loaded before any dependency and 10 | # is restricted to this project. 11 | import Config 12 | 13 | config :ex_doc, :markdown_processor, ExDoc.Markdown.Hoedown 14 | 15 | 16 | config :noizu_mnesia_versioning, 17 | topology_provider: Noizu.SimplePool.Support.TopologyProvider, 18 | schema_provider: Noizu.SimplePool.Support.SchemaProvider, 19 | mnesia_migrate_on_start: false 20 | 21 | 22 | config :logger, :console, 23 | format: "$time $metadata[$level] $message\n", 24 | metadata: [:context_token] 25 | 26 | config :noizu_scaffolding, 27 | default_nmid_generator: Noizu.SimplePool.NmidAdapter 28 | -------------------------------------------------------------------------------- /ea/SimplePool.EAP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noizu-labs/SimplePool/5ac8f82783dc5b2d104c52112523bf729eaf7cd9/ea/SimplePool.EAP -------------------------------------------------------------------------------- /ea/SimplePool.ldb: -------------------------------------------------------------------------------- 1 | DESKTOP-IVG8V3U admin -------------------------------------------------------------------------------- /lib/simple_pool/common/monitoring_framework/life_cycle_event.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.MonitoringFramework.LifeCycleEvent do 7 | 8 | @vsn 1.0 9 | @type t :: %__MODULE__{ 10 | identifier: any, 11 | time_stamp: DateTime.t, 12 | details: any 13 | } 14 | 15 | defstruct [ 16 | identifier: nil, 17 | time_stamp: nil, 18 | details: nil 19 | ] 20 | 21 | end 22 | -------------------------------------------------------------------------------- /lib/simple_pool/common/monitoring_framework/server/health_check.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.MonitoringFramework.Server.HealthCheck do 7 | alias Noizu.SimplePool.MonitoringFramework.Server.Resources 8 | alias Noizu.SimplePool.MonitoringFramework.LifeCycleEvent 9 | 10 | @vsn 1.0 11 | @type t :: %__MODULE__{ 12 | identifier: any, 13 | master_node: boolean, 14 | time_stamp: DateTime.t, 15 | status: :online | :degraged | :critical | :offline | :halting, 16 | directive: :open | :locked | :maintenance, 17 | services: %{module => Noizu.SimplePool.MonitoringFramework.Services.HealthCheck.t}, 18 | resources: Resources.t, 19 | events: [LifeCycleEvent.t], 20 | entry_point: {module, atom}, 21 | health_index: float, 22 | vsn: any 23 | } 24 | 25 | defstruct [ 26 | identifier: nil, 27 | master_node: true, 28 | time_stamp: nil, 29 | status: :offline, 30 | directive: :locked, 31 | services: nil, 32 | resources: nil, 33 | events: [], 34 | health_index: 0, 35 | entry_point: nil, 36 | vsn: @vsn 37 | ] 38 | 39 | defimpl Inspect, for: Noizu.SimplePool.MonitoringFramework.Server.HealthCheck do 40 | import Inspect.Algebra 41 | def inspect(entity, opts) do 42 | heading = "#Server.HealthCheck(#{inspect entity.identifier})" 43 | {seperator, end_seperator} = if opts.pretty, do: {"\n ", "\n"}, else: {" ", " "} 44 | inner = cond do 45 | opts.limit == :infinity -> 46 | concat(["<#{seperator}", to_doc(Map.from_struct(entity), opts), "#{end_seperator}>"]) 47 | opts.limit > 100 -> 48 | bare = %{status: entity.status, directive: entity.directive, resources: entity.resources, health_index: entity.health_index} 49 | concat(["<#{seperator}", to_doc(bare, opts), "#{end_seperator}>"]) 50 | true -> "<>" 51 | end 52 | concat [heading, inner] 53 | end # end inspect/2 54 | end # end defimpl 55 | end 56 | -------------------------------------------------------------------------------- /lib/simple_pool/common/monitoring_framework/server/resources.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.MonitoringFramework.Server.Resources do 7 | 8 | @vsn 1.0 9 | @type t :: %__MODULE__{ 10 | identifier: any, 11 | time_stamp: DateTime.t, 12 | cpu: %{nproc: integer, load: %{1 => float, 5 => float, 15 => float, 30 => float}}, 13 | ram: %{total: float, allocated: float}, 14 | vsn: any 15 | } 16 | 17 | defstruct [ 18 | identifier: nil, 19 | time_stamp: nil, 20 | cpu: %{nproc: 0, load: %{1 => 0.0, 5 => 0.0, 15 => 0.0, 30 => 0.0}}, 21 | ram: %{total: 0.0, allocated: 0.0}, 22 | vsn: @vsn 23 | ] 24 | 25 | defimpl Inspect, for: Noizu.SimplePool.MonitoringFramework.Server.Resources do 26 | import Inspect.Algebra 27 | def inspect(entity, opts) do 28 | heading = "#Server.Resources(#{inspect entity.identifier})" 29 | {seperator, end_seperator} = if opts.pretty, do: {"\n ", "\n"}, else: {" ", " "} 30 | inner = cond do 31 | opts.limit == :infinity -> 32 | concat(["<#{seperator}", to_doc(Map.from_struct(entity), opts), "#{end_seperator}>"]) 33 | opts.limit > 100 -> 34 | bare = %{time_stamp: entity.time_stamp, cpu: entity.cpu, ram: entity.ram} 35 | concat(["<#{seperator}", to_doc(bare, opts), "#{end_seperator}>"]) 36 | true -> "<>" 37 | end 38 | concat [heading, inner] 39 | end # end inspect/2 40 | end # end defimpl 41 | 42 | end 43 | -------------------------------------------------------------------------------- /lib/simple_pool/common/monitoring_framework/service/health_check.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.MonitoringFramework.Service.HealthCheck do 7 | alias Noizu.SimplePool.MonitoringFramework.Service.Definition 8 | 9 | @vsn 1.0 10 | @type t :: %__MODULE__{ 11 | identifier: any, 12 | process: pid, 13 | time_stamp: DateTime.t, 14 | status: :online | :degraded | :critical | :offline, 15 | directive: :free | :locked | :maintenance, 16 | definition: Definition.t, 17 | allocated: Map.t, 18 | health_index: float, 19 | events: [LifeCycleEvent.t], 20 | vsn: any 21 | } 22 | 23 | defstruct [ 24 | identifier: nil, 25 | process: nil, 26 | time_stamp: nil, 27 | status: :offline, 28 | directive: :locked, 29 | definition: nil, 30 | allocated: nil, 31 | health_index: 0.0, 32 | events: [], 33 | vsn: @vsn 34 | ] 35 | 36 | 37 | def template(pool, options \\ %{}) do 38 | server = options[:server] || node() 39 | %Noizu.SimplePool.MonitoringFramework.Service.HealthCheck{ 40 | identifier: {server, pool}, 41 | time_stamp: DateTime.utc_now(), 42 | status: :offline, 43 | directive: :init, 44 | definition: %Noizu.SimplePool.MonitoringFramework.Service.Definition{ 45 | identifier: {server, pool}, 46 | server: server, 47 | time_stamp: DateTime.utc_now(), 48 | pool: pool, 49 | service: Module.concat(pool, Server), 50 | supervisor: Module.concat(pool, PoolSupervisor), 51 | hard_limit: options[:hard_limit] || 250, 52 | soft_limit: options[:soft_limit] || 150, 53 | target: options[:target] || 100, 54 | }, 55 | } 56 | end 57 | 58 | 59 | defimpl Inspect, for: Noizu.SimplePool.MonitoringFramework.Service.HealthCheck do 60 | import Inspect.Algebra 61 | def inspect(entity, opts) do 62 | heading = "#Service.HealthCheck(#{inspect entity.identifier})" 63 | {seperator, end_seperator} = if opts.pretty, do: {"\n ", "\n"}, else: {" ", " "} 64 | inner = cond do 65 | opts.limit == :infinity -> 66 | concat(["<#{seperator}", to_doc(Map.from_struct(entity), opts), "#{end_seperator}>"]) 67 | opts.limit > 100 -> 68 | bare = %{status: entity.status, directive: entity.directive, allocated: entity.allocated, definition: entity.definition, health_index: entity.health_index} 69 | concat(["<#{seperator}", to_doc(bare, opts), "#{end_seperator}>"]) 70 | true -> "<>" 71 | end 72 | concat [heading, inner] 73 | end # end inspect/2 74 | end # end defimpl 75 | 76 | end 77 | -------------------------------------------------------------------------------- /lib/simple_pool/common/telemetry.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2022 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | defmodule Noizu.SimplePool.Telemetry do 6 | require Logger 7 | 8 | defp obtain_semaphore(key, count \\ 1) do 9 | try do 10 | Semaphore.acquire(key, count) 11 | rescue _e -> false 12 | catch _e -> false 13 | end 14 | end 15 | 16 | def worker_init_span(base, ref, block, context) do 17 | br = :os.system_time(:millisecond) 18 | response = block.() 19 | ar = :os.system_time(:millisecond) 20 | td = ar - br 21 | cond do 22 | td > 450 -> rate_limited_log(base, :profile, :worker_init, :very_high, context) && Logger.error(fn -> {base.banner("[Reg Time] - Critical #{__MODULE__} (#{inspect Noizu.ERP.sref(ref) } = #{td} milliseconds"), Noizu.ElixirCore.CallingContext.metadata(context) } end) 23 | td > 250 -> rate_limited_log(base, :profile, :worker_init, :high, context) && Logger.warn(fn -> {base.banner("[Reg Time] - Critical #{__MODULE__} (#{inspect Noizu.ERP.sref(ref) } = #{td} milliseconds"), Noizu.ElixirCore.CallingContext.metadata(context) } end) 24 | :else -> :ok 25 | end 26 | response 27 | end 28 | 29 | def rate_limited_log(mod, action, event, params, context, options \\ [], delay \\ 15_000, frequency \\ 5) 30 | def rate_limited_log(mod, action, event, call, _context, options, delay, frequency) do 31 | cond do 32 | options[:trace] -> true 33 | :else -> 34 | key = case call do 35 | c when is_atom(c) -> {mod, action, event, c} 36 | {d, {_, _}, {t, c, _}} when is_atom(d) and is_atom(t) and is_atom(c) -> {mod, action, event, c} 37 | {d, {_, _}, {t, c, _}} when is_atom(d) and is_atom(t) and is_tuple(c) -> {mod, action, event, elem(c, 0)} 38 | {t, c, _} when is_atom(t) and is_atom(c) -> {mod, action, event, c} 39 | {t, c, _} when is_atom(t) and is_tuple(c) -> {mod, action, event, elem(c, 0)} 40 | _ -> {mod, action, event, :other} 41 | end 42 | cond do 43 | obtain_semaphore(key, frequency) -> 44 | spawn(fn -> Process.sleep(delay) && Semaphore.release(key) end) 45 | true 46 | :else -> false 47 | end 48 | end 49 | end 50 | 51 | end 52 | 53 | -------------------------------------------------------------------------------- /lib/simple_pool/nmid_adapter.ex: -------------------------------------------------------------------------------- 1 | defmodule Noizu.SimplePool.NmidAdapter do 2 | def generate(a,b) do 3 | case Application.get_env(:noizu_scaffolding, :default_nmid_generator, nil) do 4 | nil -> 0x31337 5 | m -> m.generate(a,b) 6 | end 7 | end 8 | 9 | 10 | def generate!(a,b) do 11 | case Application.get_env(:noizu_scaffolding, :default_nmid_generator, nil) do 12 | nil -> 0x31337 13 | m -> m.generate!(a,b) 14 | end 15 | end 16 | end 17 | 18 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/behaviour.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.Behaviour do 7 | alias Noizu.ElixirCore.OptionSettings 8 | alias Noizu.ElixirCore.OptionValue 9 | alias Noizu.ElixirCore.OptionList 10 | 11 | require Logger 12 | 13 | @callback option_settings() :: Map.t 14 | 15 | @features ([:auto_identifier, :lazy_load, :async_load, :inactivity_check, :s_redirect, :s_redirect_handle, :ref_lookup_cache, :call_forwarding, :graceful_stop, :crash_protection, :migrate_shutdown]) 16 | @default_features ([:lazy_load, :s_redirect, :s_redirect_handle, :inactivity_check, :call_forwarding, :graceful_stop, :crash_protection, :migrate_shutdown]) 17 | 18 | @modules ([:worker, :server, :worker_supervisor, :pool_supervisor]) 19 | @default_modules ([:worker_supervisor, :pool_supervisor]) 20 | @methods ([:banner, :options, :option_settings]) 21 | 22 | @default_worker_options ([]) 23 | @default_server_options ([]) 24 | @default_worker_supervisor_options ([]) 25 | @default_pool_supervisor_options ([]) 26 | 27 | def prepare_options(options) do 28 | settings = %OptionSettings{ 29 | option_settings: %{ 30 | features: %OptionList{option: :features, default: Application.get_env(:noizu_simple_pool, :default_features, @default_features), valid_members: @features, membership_set: false}, 31 | only: %OptionList{option: :only, default: @methods, valid_members: @methods, membership_set: true}, 32 | override: %OptionList{option: :override, default: [], valid_members: @methods, membership_set: true}, 33 | default_modules: %OptionList{option: :default_modules, default: Application.get_env(:noizu_simple_pool, :default_modules, @default_modules), valid_members: @modules, membership_set: true}, 34 | verbose: %OptionValue{option: :verbose, default: Application.get_env(:noizu_simple_pool, :verbose, false)}, 35 | worker_lookup_handler: %OptionValue{option: :worker_lookup_handler, default: Application.get_env(:noizu_simple_pool, :worker_lookup_handler, Noizu.SimplePool.WorkerLookupBehaviour)}, 36 | worker_options: %OptionValue{option: :worker_options, default: Application.get_env(:noizu_simple_pool, :default_worker_options, @default_worker_options)}, 37 | server_options: %OptionValue{option: :server_options, default: Application.get_env(:noizu_simple_pool, :default_server_options, @default_server_options)}, 38 | worker_supervisor_options: %OptionValue{option: :worker_supervisor_options, default: Application.get_env(:noizu_simple_pool, :default_worker_supervisor_options, @default_worker_supervisor_options)}, 39 | pool_supervisor_options: %OptionValue{option: :pool_supervisor_options, default: Application.get_env(:noizu_simple_pool, :default_pool_supervisor_options, @default_pool_supervisor_options)}, 40 | worker_state_entity: %OptionValue{option: :worker_state_entity, default: :auto}, 41 | server_provider: %OptionValue{option: :server_provider, default: Application.get_env(:noizu_simple_pool, :default_server_provider, Noizu.SimplePool.Server.ProviderBehaviour.Default)}, 42 | max_supervisors: %OptionValue{option: :max_supervisors, default: Application.get_env(:noizu_simple_pool, :default_max_supervisors, 100)}, 43 | } 44 | } 45 | 46 | initial = OptionSettings.expand(settings, options) 47 | modifications = Map.take(initial.effective_options, [:worker_options, :server_options, :worker_supervisor_options, :pool_supervisor_options]) 48 | |> Enum.reduce(%{}, 49 | fn({k,v},acc) -> 50 | v = v 51 | |> Keyword.put_new(:verbose, initial.effective_options.verbose) 52 | |> Keyword.put_new(:features, initial.effective_options.features) 53 | |> Keyword.put_new(:worker_state_entity, initial.effective_options.worker_state_entity) 54 | |> Keyword.put_new(:server_provider, initial.effective_options.server_provider) 55 | Map.put(acc, k, v) 56 | end) 57 | |> Map.put(:required, List.foldl(@methods, %{}, fn(x, acc) -> Map.put(acc, x, initial.effective_options.only[x] && !initial.effective_options.override[x]) end)) 58 | 59 | %OptionSettings{initial| effective_options: Map.merge(initial.effective_options, modifications)} 60 | end 61 | 62 | def banner(header, msg) do 63 | header_len = String.length(header) 64 | len = 120 65 | 66 | sub_len = div(header_len, 2) 67 | rem = rem(header_len, 2) 68 | 69 | l_len = 59 - sub_len 70 | r_len = 59 - sub_len - rem 71 | 72 | char = "*" 73 | 74 | lines = String.split(msg, "\n", trim: true) 75 | 76 | top = "\n#{String.duplicate(char, l_len)} #{header} #{String.duplicate(char, r_len)}" 77 | bottom = String.duplicate(char, len) <> "\n" 78 | middle = for line <- lines do 79 | "#{char} " <> line 80 | end 81 | Enum.join([top] ++ middle ++ [bottom], "\n") 82 | end 83 | 84 | def expand_worker_state_entity(base_module, worker_state_entity) do 85 | if worker_state_entity == :auto do 86 | Module.concat(base_module, "WorkerStateEntity") 87 | else 88 | worker_state_entity 89 | end 90 | end 91 | 92 | defmacro __using__(options) do 93 | option_settings = prepare_options(options) 94 | options = option_settings.effective_options 95 | required = options.required 96 | #features = options.features 97 | default_modules = options.default_modules 98 | max_supervisors = options.max_supervisors 99 | quote do 100 | import unquote(__MODULE__) 101 | @behaviour Noizu.SimplePool.Behaviour 102 | @mod_name ("#{__MODULE__}") 103 | @worker_state_entity (expand_worker_state_entity(__MODULE__, unquote(options.worker_state_entity))) 104 | @max_supervisors unquote(max_supervisors) 105 | @options unquote(Macro.escape(options)) 106 | @option_settings unquote(Macro.escape(option_settings)) 107 | 108 | if (unquote(required.banner)) do 109 | def banner(msg), do: banner(@mod_name, msg) 110 | end 111 | 112 | if (unquote(required.option_settings)) do 113 | def option_settings(), do: @option_settings 114 | end 115 | 116 | if (unquote(required.options)) do 117 | def options(), do: @options 118 | end 119 | 120 | if (unquote(default_modules.worker)) do 121 | defmodule Worker do 122 | use Noizu.SimplePool.WorkerBehaviour, unquote(options.worker_options) 123 | end 124 | end 125 | 126 | if (unquote(default_modules.server)) do 127 | defmodule Server do 128 | use Noizu.SimplePool.ServerBehaviour, unquote(options.server_options) 129 | def lazy_load(state), do: state 130 | end 131 | end 132 | 133 | if (unquote(default_modules.worker_supervisor)) do 134 | module = __MODULE__ 135 | for i <- 1 .. @max_supervisors do 136 | defmodule :"#{module}.WorkerSupervisor_S#{i}" do 137 | use Noizu.SimplePool.WorkerSupervisorBehaviour, unquote(options.worker_supervisor_options) 138 | end 139 | end 140 | end 141 | 142 | if (unquote(default_modules.pool_supervisor)) do 143 | defmodule PoolSupervisor do 144 | use Noizu.SimplePool.PoolSupervisorBehaviour, unquote(options.pool_supervisor_options) 145 | end 146 | end 147 | 148 | end # end quote 149 | end #end __using__ 150 | end 151 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/monitoring_framework/environment_manager/job_manager/job_manager_pool.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.Environment.JobManagerPool do 7 | #alias Noizu.Scaffolding.CallingContext 8 | use Noizu.SimplePool.Behaviour, 9 | default_modules: [:pool_supervisor, :worker_supervisor], 10 | worker_state_entity: Noizu.Environment.JobManagerWorkerEntity, 11 | verbose: false 12 | 13 | defmodule Worker do 14 | @vsn 1.0 15 | use Noizu.SimplePool.WorkerBehaviour, 16 | worker_state_entity: Noizu.Environment.JobManagerWorkerEntity, 17 | verbose: false 18 | require Logger 19 | end # end worker 20 | 21 | #============================================================================= 22 | # @Server 23 | #============================================================================= 24 | defmodule Server do 25 | @vsn 1.0 26 | use Noizu.SimplePool.ServerBehaviour, 27 | worker_state_entity: Noizu.Environment.JobManagerWorkerEntity 28 | #alias Noizu.SimplePool.Support.TestWorkerEntity 29 | 30 | #--------------------------------------------------------------------------- 31 | # Convenience Methods 32 | #--------------------------------------------------------------------------- 33 | 34 | end # end defmodule GoldenRatio.Components.Gateway.Server 35 | end # end defmodule GoldenRatio.Components.Gateway 36 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/monitoring_framework/environment_manager/job_manager/job_manager_worker.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.Environment.JobManagerWorkerEntity do 7 | @vsn 1.0 8 | 9 | #----------------------------------------------------------------------------- 10 | # aliases, imports, uses, 11 | #----------------------------------------------------------------------------- 12 | require Logger 13 | 14 | #----------------------------------------------------------------------------- 15 | # Struct & Types 16 | #----------------------------------------------------------------------------- 17 | @type t :: %__MODULE__{ 18 | identifier: Types.entity_reference, 19 | vsn: Types.vsn 20 | } 21 | 22 | defstruct [ 23 | identifier: nil, 24 | vsn: @vsn 25 | ] 26 | 27 | use Noizu.SimplePool.InnerStateBehaviour, 28 | pool: Noizu.Environment.JobManagerPool, 29 | override: [:load] 30 | 31 | #----------------------------------------------------------------------------- 32 | # Behaviour 33 | #----------------------------------------------------------------------------- 34 | def load(ref), do: load(ref, nil, nil) 35 | def load(ref, context), do: load(ref, nil, context) 36 | def load(ref, _context, _options) do 37 | %__MODULE__{ 38 | identifier: id(ref) 39 | } 40 | end 41 | 42 | #----------------------------------------------------------------------------- 43 | # call_forwarding 44 | #----------------------------------------------------------------------------- 45 | #def call_forwarding({:test_s_call!, value}, context, _from, %__MODULE__{} = this), do: test_s_call!(this, value, context) 46 | 47 | 48 | #def call_forwarding({:test_s_cast!, value}, context, %__MODULE__{} = this), do: test_s_cast!(this, value, context) 49 | 50 | #------------------- 51 | # id/1 52 | #------------------- 53 | def id({:ref, __MODULE__, identifier}), do: identifier 54 | def id("ref.noizu-em." <> identifier), do: identifier 55 | def id(%__MODULE__{} = entity), do: entity.identifier 56 | 57 | #------------------- 58 | # ref/1 59 | #------------------- 60 | def ref({:ref, __MODULE__, identifier}), do: {:ref, __MODULE__, identifier} 61 | def ref("ref.noizu-em." <> identifier), do: {:ref, __MODULE__, identifier} 62 | def ref(%__MODULE__{} = entity), do: {:ref, __MODULE__, entity.identifier} 63 | 64 | #------------------- 65 | # sref/1 66 | #------------------- 67 | def sref({:ref, __MODULE__, identifier}), do: "ref.noizu-em.#{identifier}" 68 | def sref("ref.noizu-em." <> identifier), do: "ref.noizu-em.#{identifier}" 69 | def sref(%__MODULE__{} = entity), do: "ref.noizu-em.#{entity.identifier}" 70 | 71 | #------------------- 72 | # entity/2 73 | #------------------- 74 | def entity(ref, options \\ %{}) 75 | def entity({:ref, __MODULE__, identifier}, _options), do: %__MODULE__{identifier: identifier} 76 | def entity("ref.noizu-em." <> identifier, _options), do: %__MODULE__{identifier: identifier} 77 | def entity(%__MODULE__{} = entity, _options), do: entity 78 | 79 | #------------------- 80 | # entity!/2 81 | #------------------- 82 | def entity!(ref, options \\ %{}) 83 | def entity!({:ref, __MODULE__, identifier}, _options), do: %__MODULE__{identifier: identifier} 84 | def entity!("ref.noizu-em." <> identifier, _options), do: %__MODULE__{identifier: identifier} 85 | def entity!(%__MODULE__{} = entity, _options), do: entity 86 | 87 | 88 | #------------------- 89 | # record/2 90 | #------------------- 91 | def record(ref, options \\ %{}) 92 | def record({:ref, __MODULE__, identifier}, _options), do: %__MODULE__{identifier: identifier} 93 | def record("ref.noizu-em." <> identifier, _options), do: %__MODULE__{identifier: identifier} 94 | def record(%__MODULE__{} = entity, _options), do: entity 95 | 96 | #------------------- 97 | # record!/2 98 | #------------------- 99 | def record!(ref, options \\ %{}) 100 | def record!({:ref, __MODULE__, identifier}, _options), do: %__MODULE__{identifier: identifier} 101 | def record!("ref.noizu-em." <> identifier, _options), do: %__MODULE__{identifier: identifier} 102 | def record!(%__MODULE__{} = entity, _options), do: entity 103 | 104 | 105 | 106 | 107 | def id_ok(o) do 108 | r = ref(o) 109 | r && {:ok, r} || {:error, o} 110 | end 111 | def ref_ok(o) do 112 | r = ref(o) 113 | r && {:ok, r} || {:error, o} 114 | end 115 | def sref_ok(o) do 116 | r = sref(o) 117 | r && {:ok, r} || {:error, o} 118 | end 119 | def entity_ok(o, options \\ %{}) do 120 | r = entity(o, options) 121 | r && {:ok, r} || {:error, o} 122 | end 123 | def entity_ok!(o, options \\ %{}) do 124 | r = entity!(o, options) 125 | r && {:ok, r} || {:error, o} 126 | end 127 | 128 | 129 | 130 | defimpl Noizu.ERP, for: Noizu.Environment.JobManagerWorkerEntity do 131 | def id(obj) do 132 | obj.identifier 133 | end # end sref/1 134 | 135 | def ref(obj) do 136 | {:ref, Noizu.EnvironmentManager.JobWorkerEntity, obj.identifier} 137 | end # end ref/1 138 | 139 | def sref(obj) do 140 | "ref.noizu-em.#{obj.identifier}" 141 | end # end sref/1 142 | 143 | def record(obj, _options \\ nil) do 144 | obj 145 | end # end record/2 146 | 147 | def record!(obj, _options \\ nil) do 148 | obj 149 | end # end record/2 150 | 151 | def entity(obj, _options \\ nil) do 152 | obj 153 | end # end entity/2 154 | 155 | def entity!(obj, _options \\ nil) do 156 | obj 157 | end # end defimpl EntityReferenceProtocol, for: Tuple 158 | 159 | 160 | 161 | def id_ok(o) do 162 | r = ref(o) 163 | r && {:ok, r} || {:error, o} 164 | end 165 | def ref_ok(o) do 166 | r = ref(o) 167 | r && {:ok, r} || {:error, o} 168 | end 169 | def sref_ok(o) do 170 | r = sref(o) 171 | r && {:ok, r} || {:error, o} 172 | end 173 | def entity_ok(o, options \\ %{}) do 174 | r = entity(o, options) 175 | r && {:ok, r} || {:error, o} 176 | end 177 | def entity_ok!(o, options \\ %{}) do 178 | r = entity!(o, options) 179 | r && {:ok, r} || {:error, o} 180 | end 181 | 182 | 183 | end 184 | 185 | end # end defmacro 186 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/monitoring_framework/environment_manager/worker_entity.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.MonitoringFramework.EnvironmentWorkerEntity do 7 | @vsn 1.0 8 | 9 | #----------------------------------------------------------------------------- 10 | # aliases, imports, uses, 11 | #----------------------------------------------------------------------------- 12 | require Logger 13 | 14 | #----------------------------------------------------------------------------- 15 | # Struct & Types 16 | #----------------------------------------------------------------------------- 17 | @type t :: %__MODULE__{ 18 | identifier: Types.entity_reference, 19 | data: Map.t, 20 | vsn: Types.vsn 21 | } 22 | 23 | defstruct [ 24 | identifier: nil, 25 | data: %{}, 26 | vsn: @vsn 27 | ] 28 | 29 | use Noizu.SimplePool.InnerStateBehaviour, 30 | pool: Noizu.MonitoringFramework.EnvironmentPool, 31 | override: [:load] 32 | 33 | #----------------------------------------------------------------------------- 34 | # Behaviour 35 | #----------------------------------------------------------------------------- 36 | def load(ref), do: load(ref, nil, nil) 37 | def load(ref, context), do: load(ref, nil, context) 38 | def load(ref, _options, _context) do 39 | %__MODULE__{ 40 | identifier: id(ref) 41 | } 42 | end 43 | 44 | #----------------------------------------------------------------------------- 45 | # Implementation 46 | #----------------------------------------------------------------------------- 47 | def test_s_call!(this, value, _context) do 48 | state = put_in(this, [Access.key(:data), :s_call!], value) 49 | {:reply, :s_call!, state} 50 | end 51 | def test_s_call(this, value, _context), do: {:reply, :s_call, put_in(this, [Access.key(:data), :s_call], value)} 52 | def test_s_cast!(this, value, _context), do: {:noreply, put_in(this, [Access.key(:data), :s_cast!], value)} 53 | def test_s_cast(this, value, _context), do: {:noreply, put_in(this, [Access.key(:data), :s_cast], value)} 54 | 55 | #----------------------------------------------------------------------------- 56 | # call_forwarding 57 | #----------------------------------------------------------------------------- 58 | def call_forwarding({:test_s_call!, value}, context, _from, %__MODULE__{} = this), do: test_s_call!(this, value, context) 59 | def call_forwarding({:test_s_call, value}, context, _from, %__MODULE__{} = this), do: test_s_call(this, value, context) 60 | 61 | 62 | def call_forwarding({:test_s_cast!, value}, context, %__MODULE__{} = this), do: test_s_cast!(this, value, context) 63 | def call_forwarding({:test_s_cast, value}, context, %__MODULE__{} = this), do: test_s_cast(this, value, context) 64 | 65 | #------------------- 66 | # id/1 67 | #------------------- 68 | def id({:ref, __MODULE__, identifier}), do: identifier 69 | def id("ref.noizu-env." <> identifier), do: identifier 70 | def id(%__MODULE__{} = entity), do: entity.identifier 71 | 72 | #------------------- 73 | # ref/1 74 | #------------------- 75 | def ref({:ref, __MODULE__, identifier}), do: {:ref, __MODULE__, identifier} 76 | def ref("ref.noizu-env." <> identifier), do: {:ref, __MODULE__, identifier} 77 | def ref(%__MODULE__{} = entity), do: {:ref, __MODULE__, entity.identifier} 78 | 79 | #------------------- 80 | # sref/1 81 | #------------------- 82 | def sref({:ref, __MODULE__, identifier}), do: "ref.noizu-env.#{identifier}" 83 | def sref("ref.noizu-env." <> identifier), do: "ref.noizu-env.#{identifier}" 84 | def sref(%__MODULE__{} = entity), do: "ref.noizu-env.#{entity.identifier}" 85 | 86 | #------------------- 87 | # entity/2 88 | #------------------- 89 | def entity(ref, options \\ %{}) 90 | def entity({:ref, __MODULE__, identifier}, _options), do: %__MODULE__{identifier: identifier} 91 | def entity("ref.noizu-env." <> identifier, _options), do: %__MODULE__{identifier: identifier} 92 | def entity(%__MODULE__{} = entity, _options), do: entity 93 | 94 | #------------------- 95 | # entity!/2 96 | #------------------- 97 | def entity!(ref, options \\ %{}) 98 | def entity!({:ref, __MODULE__, identifier}, _options), do: %__MODULE__{identifier: identifier} 99 | def entity!("ref.noizu-env." <> identifier, _options), do: %__MODULE__{identifier: identifier} 100 | def entity!(%__MODULE__{} = entity, _options), do: entity 101 | 102 | 103 | #------------------- 104 | # record/2 105 | #------------------- 106 | def record(ref, options \\ %{}) 107 | def record({:ref, __MODULE__, identifier}, _options), do: %__MODULE__{identifier: identifier} 108 | def record("ref.noizu-env." <> identifier, _options), do: %__MODULE__{identifier: identifier} 109 | def record(%__MODULE__{} = entity, _options), do: entity 110 | 111 | #------------------- 112 | # record!/2 113 | #------------------- 114 | def record!(ref, options \\ %{}) 115 | def record!({:ref, __MODULE__, identifier}, _options), do: %__MODULE__{identifier: identifier} 116 | def record!("ref.noizu-env." <> identifier, _options), do: %__MODULE__{identifier: identifier} 117 | def record!(%__MODULE__{} = entity, _options), do: entity 118 | 119 | 120 | 121 | def id_ok(o) do 122 | r = ref(o) 123 | r && {:ok, r} || {:error, o} 124 | end 125 | def ref_ok(o) do 126 | r = ref(o) 127 | r && {:ok, r} || {:error, o} 128 | end 129 | def sref_ok(o) do 130 | r = sref(o) 131 | r && {:ok, r} || {:error, o} 132 | end 133 | def entity_ok(o, options \\ %{}) do 134 | r = entity(o, options) 135 | r && {:ok, r} || {:error, o} 136 | end 137 | def entity_ok!(o, options \\ %{}) do 138 | r = entity!(o, options) 139 | r && {:ok, r} || {:error, o} 140 | end 141 | 142 | 143 | defimpl Noizu.ERP, for: Noizu.MonitoringFramework.EnvironmentWorkerEntity do 144 | def id(obj) do 145 | obj.identifier 146 | end # end sref/1 147 | 148 | def ref(obj) do 149 | {:ref, Noizu.MonitoringFramework.EnvironmentWorkerEntity, obj.identifier} 150 | end # end ref/1 151 | 152 | def sref(obj) do 153 | "ref.noizu-env.#{obj.identifier}" 154 | end # end sref/1 155 | 156 | def record(obj, _options \\ nil) do 157 | obj 158 | end # end record/2 159 | 160 | def record!(obj, _options \\ nil) do 161 | obj 162 | end # end record/2 163 | 164 | def entity(obj, _options \\ nil) do 165 | obj 166 | end # end entity/2 167 | 168 | def entity!(obj, _options \\ nil) do 169 | obj 170 | end # end defimpl EntityReferenceProtocol, for: Tuple 171 | 172 | 173 | 174 | def id_ok(o) do 175 | r = ref(o) 176 | r && {:ok, r} || {:error, o} 177 | end 178 | def ref_ok(o) do 179 | r = ref(o) 180 | r && {:ok, r} || {:error, o} 181 | end 182 | def sref_ok(o) do 183 | r = sref(o) 184 | r && {:ok, r} || {:error, o} 185 | end 186 | def entity_ok(o, options \\ %{}) do 187 | r = entity(o, options) 188 | r && {:ok, r} || {:error, o} 189 | end 190 | def entity_ok!(o, options \\ %{}) do 191 | r = entity!(o, options) 192 | r && {:ok, r} || {:error, o} 193 | end 194 | 195 | 196 | end 197 | 198 | end # end defmacro 199 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/monitoring_framework/monitor_behaviour.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.MonitoringFramework.MonitorBehaviour do 7 | @type job_handle :: pid | function | mfa | {:ref, module, any} 8 | @type job_status :: {:error, details :: any} | {:completed, integer} | {:started, integer} | {:queued, integer} 9 | @type job_response :: {:ok, job_handle} | {:error, details :: any} 10 | @type server_group :: list | :all | MapSet.t | Map.t 11 | @type component_group :: list | :all | MapSet.t | Map.t 12 | 13 | @callback supports_service?(server :: any, component :: any, Noizu.ElixirCore.CallingContext.t | nil, opts :: Map.t) :: :ack | :nack | any 14 | 15 | @callback rebalance(input :: server_group, output :: server_group, components :: component_group, Noizu.ElixirCore.CallingContext.t | nil, opts :: Map.t) :: job_response 16 | 17 | @callback lock_server(input :: server_group, components :: component_group, Noizu.ElixirCore.CallingContext.t | nil, opts :: Map.t) :: job_response 18 | @callback release_server(input :: server_group, components :: component_group, Noizu.ElixirCore.CallingContext.t | nil, opts :: Map.t) :: job_response 19 | 20 | #@callback join(server :: atom, settings :: Map.t, Noizu.ElixirCore.CallingContext.t | nil, opts :: Map.t) :: job_response 21 | #@callback leave(server :: atom, settings :: Map.t, Noizu.ElixirCore.CallingContext.t | nil, opts :: Map.t) :: job_response 22 | 23 | @callback select_host(ref :: any, server :: atom, Noizu.ElixirCore.CallingContext.t | nil, opts :: Map.t) :: {:ack, atom} | {:nack, details :: any} | {:error, details :: any} 24 | 25 | @callback record_server_event!(server :: atom, event :: atom, details :: any, Noizu.ElixirCore.CallingContext.t | nil, opts :: Map.t) :: :ack | :nack | {:error, details :: any} 26 | @callback record_service_event!(server :: atom, service :: module, event :: atom, details :: any, Noizu.ElixirCore.CallingContext.t | nil, opts :: Map.t) :: :ack | :nack | {:error, details :: any} 27 | end 28 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/monitoring_framework/monitor_behaviour/default.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.MonitoringFramework.MonitorBehaviour.Default do 7 | require Logger 8 | @behaviour Noizu.SimplePool.MonitoringFramework.MonitorBehaviour 9 | 10 | def supports_service?(_server, _component, _context, _options \\ %{}) do 11 | :ack 12 | end 13 | 14 | def rebalance(_input, _output, _components, _context, _options \\ %{}) do 15 | {:ack, self()} 16 | end 17 | 18 | def lock_server(_servers, _components, _context, _options \\ %{}) do 19 | {:ack, self()} 20 | end 21 | 22 | def release_server(_servers, _components, _context, _options \\ %{}) do 23 | {:ack, self()} 24 | end 25 | 26 | def select_host(_ref, _component, _context, _options \\ %{}) do 27 | {:ack, node()} 28 | end 29 | 30 | 31 | def record_server_event!(_server, _event, _details, _context, _options \\ %{}), do: :ack 32 | def record_service_event!(_server, _service, _event, _details, _context, _options \\ %{}), do: :ack 33 | 34 | end 35 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/monitoring_framework/service/definition.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.MonitoringFramework.Service.Definition do 7 | @vsn 1.0 8 | @type t :: %__MODULE__{ 9 | identifier: any, 10 | server: atom, 11 | pool: module, 12 | service: module, 13 | supervisor: module, 14 | server_options: any, 15 | worker_sup_options: any, 16 | time_stamp: DateTime.t, 17 | hard_limit: integer, 18 | soft_limit: integer, 19 | target: integer, 20 | vsn: any 21 | } 22 | 23 | defstruct [ 24 | identifier: nil, 25 | server: nil, 26 | pool: nil, 27 | service: nil, 28 | supervisor: nil, 29 | server_options: nil, 30 | worker_sup_options: nil, 31 | time_stamp: nil, 32 | hard_limit: 0, 33 | soft_limit: 0, 34 | target: 0, 35 | vsn: @vsn 36 | ] 37 | 38 | defimpl Inspect, for: Noizu.SimplePool.MonitoringFramework.Service.Definition do 39 | import Inspect.Algebra 40 | def inspect(entity, opts) do 41 | heading = "#Service.Definition(#{inspect entity.identifier})" 42 | {seperator, end_seperator} = if opts.pretty, do: {"\n ", "\n"}, else: {" ", " "} 43 | inner = cond do 44 | opts.limit == :infinity -> 45 | concat(["<#{seperator}", to_doc(Map.from_struct(entity), opts), "#{end_seperator}>"]) 46 | opts.limit > 100 -> 47 | bare = %{hard_limit: entity.hard_limit, soft_limit: entity.soft_limit, target: entity.target} 48 | concat(["<#{seperator}", to_doc(bare, opts), "#{end_seperator}>"]) 49 | true -> "<>" 50 | end 51 | concat [heading, inner] 52 | end # end inspect/2 53 | end # end defimpl 54 | end 55 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/server/environment_details.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.Server.EnvironmentDetails do 7 | @type t :: %__MODULE__{ 8 | server: any, 9 | definition: any, 10 | initial: Noizu.SimplePool.MonitoringFramework.Service.HealthCheck.t, 11 | effective: Noizu.SimplePool.MonitoringFramework.Service.HealthCheck.t, 12 | default: any, 13 | status: atom, 14 | monitors: Map.t, 15 | } 16 | 17 | defstruct [ 18 | server: nil, 19 | definition: nil, 20 | initial: nil, 21 | effective: nil, 22 | default: nil, 23 | status: nil, 24 | monitors: %{} 25 | ] 26 | 27 | end 28 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/server/provider_behaviour.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.Server.ProviderBehaviour do 7 | #------------------------------------------------------------------------------- 8 | # GenServer Lifecycle 9 | #------------------------------------------------------------------------------- 10 | @callback init(module :: Module, sup :: any, context :: any, options :: Noizu.SimplePool.OptionSettings.t) :: any 11 | @callback terminate(server :: Module, reason :: any, state :: Noizu.SimplePool.Server.State.t, context :: any, options :: any) :: any 12 | 13 | #------------------------------------------------------------------------------- 14 | # Startup: Lazy Loading/Async Load/Immediate Load strategies. Blocking/Lazy Initialization, Loading Strategy. 15 | #------------------------------------------------------------------------------- 16 | @callback status(module :: Module, context :: any) :: any 17 | @callback load(module :: Module, context :: any, options :: any) :: any 18 | @callback load_complete(any, any, any) :: any 19 | 20 | #------------------------------------------------------------------------------- 21 | # Internal Routing 22 | #------------------------------------------------------------------------------- 23 | @callback internal_call_handler({:i, call :: any, context :: any}, from :: any, state :: Noizu.SimplePool.Server.State.t) :: {:reply, any, Noizu.SimplePool.Server.State.t} 24 | @callback internal_cast_handler({:i, call :: any, context :: any}, state :: Noizu.SimplePool.Server.State.t) :: {:noreply, Noizu.SimplePool.Server.State.t} 25 | @callback internal_info_handler({:i, call :: any, context :: any}, state :: Noizu.SimplePool.Server.State.t) :: {:noreply, Noizu.SimplePool.Server.State.t} 26 | end 27 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/server/state.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.Server.State do 7 | alias Noizu.SimplePool.Server.State 8 | 9 | @type t :: %State{ 10 | worker_supervisor: any, 11 | service: any, 12 | status_details: any, 13 | status: Map.t, 14 | extended: any, 15 | entity: any, 16 | environment_details: Noizu.SimplePool.Server.EnvironmentDetails.t, 17 | options: Noizu.SimplePool.OptionSettings.t 18 | } 19 | 20 | defstruct [ 21 | worker_supervisor: nil, 22 | service: nil, 23 | status_details: nil, 24 | status: %{loading: :pending, state: :pending}, 25 | extended: %{}, 26 | entity: nil, 27 | environment_details: nil, 28 | options: nil 29 | ] 30 | 31 | #----------------------------------------------------------------------------- 32 | # Inspect Protocol 33 | #----------------------------------------------------------------------------- 34 | defimpl Inspect, for: Noizu.SimplePool.Server.State do 35 | import Inspect.Algebra 36 | def inspect(entity, opts) do 37 | heading = "#Server.State(#{inspect entity.service})" 38 | {seperator, end_seperator} = if opts.pretty, do: {"\n ", "\n"}, else: {" ", " "} 39 | inner = cond do 40 | opts.limit == :infinity -> 41 | concat(["<#{seperator}", to_doc(Map.from_struct(entity), opts), "#{end_seperator}>"]) 42 | true -> "<>" 43 | end 44 | concat [heading, inner] 45 | end # end inspect/2 46 | end # end defimpl 47 | 48 | end 49 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/support/database.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | use Amnesia 7 | 8 | defdatabase Noizu.SimplePool.Database do 9 | #-------------------------------------- 10 | # FastGlobal Cluster 11 | #-------------------------------------- 12 | 13 | #-------------------------------------- 14 | # Dispatch 15 | #-------------------------------------- 16 | deftable DispatchTable, [:identifier, :server, :entity], type: :set, index: [] do 17 | @type t :: %DispatchTable{identifier: tuple, server: atom, entity: Noizu.SimplePool.DispatchEntity.t} 18 | end 19 | 20 | deftable Dispatch.MonitorTable, [:identifier, :time, :event, :details], type: :bag, index: [] do 21 | @type t :: %Dispatch.MonitorTable{identifier: any, time: integer, event: any, details: any} 22 | end 23 | 24 | #-------------------------------------- 25 | # Monitoring Framework 26 | #-------------------------------------- 27 | deftable MonitoringFramework.SettingTable, [:setting, :value], type: :bag, index: [] do 28 | @type t :: %MonitoringFramework.SettingTable{setting: atom, value: any} 29 | end 30 | 31 | deftable MonitoringFramework.NodeTable, [:identifier, :status, :directive, :health_index, :entity], type: :set, index: [] do 32 | @type t :: %MonitoringFramework.NodeTable{identifier: any, status: atom, directive: atom, health_index: float, entity: Noizu.SimplePool.MonitoringFramework.Server.HealthCheck.t} 33 | end 34 | 35 | deftable MonitoringFramework.Node.EventTable, [:identifier, :event, :time_stamp, :entity], type: :bag, index: [] do 36 | @type t :: %MonitoringFramework.Node.EventTable{identifier: atom, event: atom, time_stamp: integer, entity: Noizu.SimplePool.MonitoringFramework.LifeCycleEvent.t} 37 | end 38 | 39 | deftable MonitoringFramework.ServiceTable, [:identifier, :status, :directive, :health_index, :entity], type: :set, index: [] do 40 | @type t :: %MonitoringFramework.ServiceTable{identifier: {atom, atom}, status: atom, directive: atom, health_index: float, entity: Noizu.SimplePool.MonitoringFramework.Service.HealthCheck.t} 41 | end 42 | 43 | deftable MonitoringFramework.Service.EventTable, [:identifier, :event, :time_stamp, :entity], type: :bag, index: [] do 44 | @type t :: %MonitoringFramework.Service.EventTable{identifier: {atom, atom}, event: atom, time_stamp: integer, entity: Noizu.SimplePool.MonitoringFramework.LifeCycleEvent.t} 45 | end 46 | 47 | deftable MonitoringFramework.Service.HintTable, [:identifier, :hint, :time_stamp, :status], type: :set, index: [] do 48 | @type t :: %MonitoringFramework.Service.HintTable{identifier: atom, hint: Map.t, time_stamp: integer, status: atom} 49 | end 50 | 51 | end 52 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/support/dispatch_entity.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.DispatchEntity do 7 | @type t :: %__MODULE__{ 8 | identifier: any, 9 | state: atom, 10 | server: atom, 11 | lock: nil | {{atom, pid}, atom, integer} 12 | } 13 | 14 | defstruct [ 15 | identifier: nil, 16 | state: :spawning, 17 | server: nil, 18 | lock: nil 19 | ] 20 | 21 | def id(%__MODULE__{} = e), do: e.identifier 22 | def id({:ref, __MODULE__, identifier}), do: identifier 23 | 24 | def ref(%__MODULE__{} = e), do: {:ref, __MODULE__, e.identifier} 25 | def ref({:ref, __MODULE__, identifier}), do: {:ref, __MODULE__, identifier} 26 | 27 | def sref(%__MODULE__{} = e), do: "ref.noizu-dispatch.[#{Noizu.ERP.sref(e.identifier)}]" 28 | def sref({:ref, __MODULE__, identifier}), do: "ref.noizu-dispatch.#{identifier}" 29 | 30 | def entity(ref, options \\ %{}) 31 | def entity({:ref, __MODULE__, identifier}, options), do: Noizu.SimplePool.DispatchRepo.get(identifier, options[:context], options) 32 | def entity(%__MODULE__{} = e, _options), do: e 33 | 34 | def entity!(ref, options \\ %{}) 35 | def entity!({:ref, __MODULE__, identifier}, options), do: Noizu.SimplePool.DispatchRepo.get(identifier, options[:context], options) 36 | def entity!(%__MODULE__{} = e, _options), do: e 37 | 38 | def record(ref, options \\ %{}) 39 | def record({:ref, __MODULE__, identifier}, options) do 40 | entity = Noizu.SimplePool.DispatchRepo.get(identifier, options[:context], options) 41 | entity && %Noizu.SimplePool.Database.DispatchTable{identifier: entity.identifier, server: entity.server, entity: entity} 42 | end 43 | def record(%__MODULE__{} = entity, _options) do 44 | %Noizu.SimplePool.Database.DispatchTable{identifier: entity.identifier, server: entity.server, entity: entity} 45 | end 46 | 47 | def record!(ref, options \\ %{}) 48 | def record!({:ref, __MODULE__, identifier}, options) do 49 | entity = Noizu.SimplePool.DispatchRepo.get!(identifier, options[:context], options) 50 | entity && %Noizu.SimplePool.Database.DispatchTable{identifier: entity.identifier, server: entity.server, entity: entity} 51 | end 52 | def record!(%__MODULE__{} = entity, _options) do 53 | %Noizu.SimplePool.Database.DispatchTable{identifier: entity.identifier, server: entity.server, entity: entity} 54 | end 55 | 56 | def has_permission(_ref, _permission, _context), do: true 57 | def has_permission!(_ref, _permission, _context), do: true 58 | 59 | defimpl Noizu.ERP, for: Noizu.SimplePool.DispatchEntity do 60 | def id(obj) do 61 | obj.identifier 62 | end # end sref/1 63 | 64 | def ref(obj) do 65 | {:ref, Noizu.SimplePool.DispatchEntity, obj.identifier} 66 | end # end ref/1 67 | 68 | def sref(obj) do 69 | "ref.noizu-dispatch.[#{Noizu.ERP.sref(obj.identifier)}]" 70 | end # end sref/1 71 | 72 | def record(obj, _options \\ nil) do 73 | %Noizu.SimplePool.Database.DispatchTable{identifier: obj.identifier, server: obj.server, entity: obj} 74 | end # end record/2 75 | 76 | def record!(obj, _options \\ nil) do 77 | %Noizu.SimplePool.Database.DispatchTable{identifier: obj.identifier, server: obj.server, entity: obj} 78 | end # end record/2 79 | 80 | def entity(obj, _options \\ nil) do 81 | obj 82 | end # end entity/2 83 | 84 | def entity!(obj, _options \\ nil) do 85 | obj 86 | end # end defimpl EntityReferenceProtocol, for: Tuple 87 | 88 | 89 | def id_ok(o) do 90 | r = id(o) 91 | r && {:ok, r} || {:error, o} 92 | end 93 | def ref_ok(o) do 94 | r = ref(o) 95 | r && {:ok, r} || {:error, o} 96 | end 97 | def sref_ok(o) do 98 | r = sref(o) 99 | r && {:ok, r} || {:error, o} 100 | end 101 | def entity_ok(o, options \\ %{}) do 102 | r = entity(o, options) 103 | r && {:ok, r} || {:error, o} 104 | end 105 | def entity_ok!(o, options \\ %{}) do 106 | r = entity!(o, options) 107 | r && {:ok, r} || {:error, o} 108 | end 109 | end 110 | 111 | 112 | end 113 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/support/dispatch_monitor_repo.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.Dispatch.MonitorRepo do 7 | use Noizu.SimplePool.DispatchMonitorRepoBehaviour, 8 | monitor_table: Noizu.SimplePool.Database.Dispatch.MonitorTable 9 | end 10 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/support/dispatch_monitor_repo_behaviour.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.DispatchMonitorRepoBehaviour do 7 | 8 | defmacro __using__(options) do 9 | monitor_table = options[:monitor_table] 10 | 11 | quote do 12 | @monitor_table unquote(monitor_table) 13 | 14 | def schema_online?() do 15 | case Amnesia.Table.wait([@monitor_table], 5) do 16 | :ok -> true 17 | _ -> false 18 | end 19 | end 20 | 21 | def new(ref, event, details, _context, options \\ %{}) do 22 | time = options[:time] || :os.system_time(:seconds) 23 | %@monitor_table{identifier: ref, time: time, event: event, details: details} 24 | end 25 | 26 | #------------------------- 27 | # 28 | #------------------------- 29 | def get!(id, _context, _options \\ %{}) do 30 | if schema_online?() do 31 | id |> @monitor_table.read!() 32 | else 33 | nil 34 | end 35 | end 36 | 37 | def update!(entity, _context, _options \\ %{}) do 38 | if schema_online?() do 39 | entity 40 | |> @monitor_table.write!() 41 | else 42 | nil 43 | end 44 | end 45 | 46 | def create!(entity, _context, _options \\ %{}) do 47 | if schema_online?() do 48 | entity 49 | |> @monitor_table.write!() 50 | else 51 | nil 52 | end 53 | end 54 | 55 | def delete!(entity, _context, _options \\ %{}) do 56 | if schema_online?() do 57 | @monitor_table.delete!(entity) 58 | end 59 | entity 60 | end 61 | 62 | #------------------------- 63 | # 64 | #------------------------- 65 | def get(id, _context, _options \\ %{}) do 66 | if schema_online?() do 67 | id |> @monitor_table.read() 68 | else 69 | nil 70 | end 71 | end 72 | 73 | def update(entity, _context, _options \\ %{}) do 74 | if schema_online?() do 75 | entity 76 | |> @monitor_table.write() 77 | else 78 | nil 79 | end 80 | end 81 | 82 | def create(entity, _context, _options \\ %{}) do 83 | if schema_online?() do 84 | entity 85 | |> @monitor_table.write() 86 | else 87 | nil 88 | end 89 | end 90 | 91 | def delete(entity, _context, _options \\ %{}) do 92 | if schema_online?() do 93 | @monitor_table.delete(entity) 94 | end 95 | entity 96 | end 97 | 98 | defimpl Inspect, for: @monitor_table do 99 | import Inspect.Algebra 100 | def inspect(entity, opts) do 101 | heading = "#WorkerEvent(#{entity.event},#{entity.time})" 102 | {seperator, end_seperator} = if opts.pretty, do: {"\n ", "\n"}, else: {" ", " "} 103 | inner = cond do 104 | opts.limit == :infinity -> 105 | concat(["<#{seperator}", to_doc(Map.from_struct(entity), opts), "#{seperator}>"]) 106 | true -> "<>" 107 | end 108 | concat [heading, inner] 109 | end # end inspect/2 110 | end # end defimpl 111 | end 112 | end 113 | end 114 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/support/dispatch_repo.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.DispatchRepo do 7 | use Noizu.SimplePool.DispatchRepoBehaviour, 8 | dispatch_table: Noizu.SimplePool.Database.DispatchTable 9 | end 10 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/support/dispatch_repo_behaviour.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.DispatchRepoBehaviour do 7 | use Amnesia 8 | 9 | 10 | defmacro __using__(options) do 11 | dispatch_table = options[:dispatch_table] 12 | 13 | quote do 14 | @dispatch_table unquote(dispatch_table) 15 | use Amnesia 16 | import unquote(__MODULE__) 17 | 18 | def schema_online?() do 19 | case Amnesia.Table.wait([@dispatch_table], 5) do 20 | :ok -> true 21 | _ -> false 22 | end 23 | end 24 | 25 | def prepare_lock(options, force \\ false) do 26 | Noizu.SimplePool.DispatchRepoBehaviourDefault.prepare_lock(options, force) 27 | end 28 | 29 | def new(ref, context, options \\ %{}) do 30 | Noizu.SimplePool.DispatchRepoBehaviourDefault.new(__MODULE__, ref, context, options) 31 | end 32 | 33 | def obtain_lock!(ref, context, options \\ %{lock: %{}}) do 34 | Noizu.SimplePool.DispatchRepoBehaviourDefault.obtain_lock!(__MODULE__, ref, context, options) 35 | end 36 | 37 | def release_lock!(ref, context, options \\ %{}) do 38 | Noizu.SimplePool.DispatchRepoBehaviourDefault.release_lock!(__MODULE__, ref, context, options) 39 | end 40 | 41 | def workers!(host, service_entity, _context, options \\ %{}) do 42 | if schema_online?() do 43 | v = @dispatch_table.match!([identifier: {:ref, service_entity, :_}, server: host]) 44 | |> Amnesia.Selection.values 45 | {:ack, v} 46 | else 47 | {:nack, []} 48 | end 49 | end 50 | 51 | #------------------------- 52 | # 53 | #------------------------- 54 | def get!(id, _context, _options \\ %{}) do 55 | if schema_online?() do 56 | id 57 | |> @dispatch_table.read!() 58 | |> Noizu.ERP.entity() 59 | else 60 | nil 61 | end 62 | end 63 | 64 | def update!(%Noizu.SimplePool.DispatchEntity{} = entity, _context, options \\ %{}) do 65 | if schema_online?() do 66 | %@dispatch_table{identifier: entity.identifier, server: entity.server, entity: entity} 67 | |> @dispatch_table.write!() 68 | |> Noizu.ERP.entity() 69 | else 70 | nil 71 | end 72 | 73 | end 74 | 75 | def create!(%Noizu.SimplePool.DispatchEntity{} = entity, _context, options \\ %{}) do 76 | if schema_online?() do 77 | %@dispatch_table{identifier: entity.identifier, server: entity.server, entity: entity} 78 | |> @dispatch_table.write!() 79 | |> Noizu.ERP.entity() 80 | else 81 | nil 82 | end 83 | end 84 | 85 | def delete!(entity, _context, _options \\ %{}) do 86 | if schema_online?() do 87 | @dispatch_table.delete!(entity.identifier) 88 | end 89 | entity 90 | end 91 | 92 | #------------------------- 93 | # 94 | #------------------------- 95 | def get(id, _context, _options \\ %{}) do 96 | if schema_online?() do 97 | id 98 | |> @dispatch_table.read() 99 | |> Noizu.ERP.entity() 100 | else 101 | nil 102 | end 103 | end 104 | 105 | def update(%Noizu.SimplePool.DispatchEntity{} = entity, _context, options \\ %{}) do 106 | if schema_online?() do 107 | %@dispatch_table{identifier: entity.identifier, server: entity.server, entity: entity} 108 | |> @dispatch_table.write() 109 | |> Noizu.ERP.entity() 110 | else 111 | nil 112 | end 113 | end 114 | 115 | def create(%Noizu.SimplePool.DispatchEntity{} = entity, _context, options \\ %{}) do 116 | if schema_online?() do 117 | %@dispatch_table{identifier: entity.identifier, server: entity.server, entity: entity} 118 | |> @dispatch_table.write() 119 | |> Noizu.ERP.entity() 120 | else 121 | nil 122 | end 123 | end 124 | 125 | def delete(entity, _context, _options \\ %{}) do 126 | if schema_online?() do 127 | @dispatch_table.delete(entity.identifier) 128 | entity 129 | else 130 | nil 131 | end 132 | end 133 | 134 | defimpl Noizu.ERP, for: @dispatch_table do 135 | def id(obj) do 136 | obj.identifier 137 | end # end sref/1 138 | 139 | def ref(obj) do 140 | {:ref, Noizu.SimplePool.DispatchEntity, obj.identifier} 141 | end # end ref/1 142 | 143 | def sref(obj) do 144 | "ref.noizu-dispatch.[#{Noizu.ERP.sref(obj.identifier)}]" 145 | end # end sref/1 146 | 147 | def record(obj, _options \\ nil) do 148 | obj 149 | end # end record/2 150 | 151 | def record!(obj, _options \\ nil) do 152 | obj 153 | end # end record/2 154 | 155 | def entity(obj, _options \\ nil) do 156 | obj.entity 157 | end # end entity/2 158 | 159 | def entity!(obj, _options \\ nil) do 160 | obj.entity 161 | end # end defimpl EntityReferenceProtocol, for: Tuple 162 | 163 | 164 | def id_ok(o) do 165 | r = id(o) 166 | r && {:ok, r} || {:error, o} 167 | end 168 | def ref_ok(o) do 169 | r = ref(o) 170 | r && {:ok, r} || {:error, o} 171 | end 172 | def sref_ok(o) do 173 | r = sref(o) 174 | r && {:ok, r} || {:error, o} 175 | end 176 | def entity_ok(o, options \\ %{}) do 177 | r = entity(o, options) 178 | r && {:ok, r} || {:error, o} 179 | end 180 | def entity_ok!(o, options \\ %{}) do 181 | r = entity!(o, options) 182 | r && {:ok, r} || {:error, o} 183 | end 184 | 185 | end # end defimpl 186 | end # end quote 187 | end # end __suing__ 188 | end # end module 189 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/support/dispatch_repo_behaviour_default.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.DispatchRepoBehaviourDefault do 7 | use Amnesia 8 | 9 | def new(mod, ref, _context, options \\ %{}) do 10 | state = options[:state] || :new 11 | server = options[:server] || :pending 12 | lock = mod.prepare_lock(options) 13 | %Noizu.SimplePool.DispatchEntity{identifier: ref, server: server, state: state, lock: lock} 14 | end 15 | 16 | def prepare_lock(options, force \\ false) do 17 | if options[:lock] || force do 18 | time = options[:time] || :os.system_time() 19 | lock_server = options[:lock][:server] || node() 20 | lock_process = options[:lock][:process] || self() 21 | lock_until = (options[:lock][:until]) || (options[:lock][:for] && :os.system_time(:seconds) + options[:lock][:for]) || (time + 5 + :rand.uniform(15)) 22 | lock_type = options[:lock][:type] || :spawn 23 | {{lock_server, lock_process}, lock_type, lock_until} 24 | else 25 | nil 26 | end 27 | end 28 | 29 | def obtain_lock!(mod, ref, context, options \\ %{lock: %{}}) do 30 | if mod.schema_online?() do 31 | lock = {{lock_server, lock_process}, _lock_type, _lock_until} = mod.prepare_lock(options, true) 32 | entity = mod.get!(ref, context, options) 33 | time = options[:time] || :os.system_time() 34 | if entity do 35 | case entity.lock do 36 | nil -> {:ack, put_in(entity, [Access.key(:lock)], lock) |> mod.update!(context, options)} 37 | {{s,p}, _lt, lu} -> 38 | cond do 39 | options[:force] -> {:ack, put_in(entity, [Access.key(:lock)], lock) |> mod.update!(context, options)} 40 | time > lu -> {:ack, put_in(entity, [Access.key(:lock)], lock) |> mod.update!(context, options)} 41 | s == lock_server and p == lock_process -> {:ack, put_in(entity, [Access.key(:lock)], lock) |> mod.update!(context, options)} 42 | options[:conditional_checkout] -> 43 | check = case options[:conditional_checkout] do 44 | v when is_function(v) -> v.(entity) 45 | {m,f,1} -> apply(m, f, [entity]) 46 | _ -> false 47 | end 48 | if check do 49 | {:ack, put_in(entity, [Access.key(:lock)], lock) |> mod.update!(context, options)} 50 | else 51 | {:nack, {:locked, entity}} 52 | end 53 | true -> {:nack, {:locked, entity}} 54 | end 55 | _o -> {:nack, {:invalid, entity}} 56 | end 57 | else 58 | e = mod.new(ref, context, options) 59 | |> put_in([Access.key(:lock)], lock) 60 | |> mod.create!(context, options) 61 | {:ack, e} 62 | end 63 | else 64 | {:nack, {:error, :schema_offline}} 65 | end 66 | end 67 | 68 | def release_lock!(mod, ref, context, options \\ %{}) do 69 | if mod.schema_online?() do 70 | time = options[:time] || :os.system_time() 71 | entity = mod.get!(ref, context, options) 72 | if entity do 73 | case entity.lock do 74 | nil -> {:ack, entity} 75 | {{s,p}, lt, lu} -> 76 | _lock = {{lock_server, lock_process}, lock_type, _lock_until} = mod.prepare_lock(options, true) 77 | cond do 78 | options[:force] -> 79 | {:ack, put_in(entity, [Access.key(:lock)], nil) |> mod.update!(context, options)} 80 | time > lu -> 81 | {:ack, put_in(entity, [Access.key(:lock)], nil) |> mod.update!(context, options)} 82 | s == lock_server and p == lock_process and lt == lock_type -> {:ack, put_in(entity, [Access.key(:lock)], nil) |> mod.update!(context, options)} 83 | true -> {:nack, {:not_owned, entity}} 84 | end 85 | end 86 | else 87 | {:ack, nil} 88 | end 89 | else 90 | {:nack, {:error, :schema_offline}} 91 | end 92 | end 93 | end # end module 94 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/support/schema/core.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.Support.Schema.Core do 7 | alias Noizu.MnesiaVersioning.ChangeSet 8 | use Amnesia 9 | use Noizu.SimplePool.Database 10 | use Noizu.MnesiaVersioning.SchemaBehaviour 11 | 12 | 13 | def neighbors() do 14 | topology_provider = Application.get_env(:noizu_mnesia_versioning, :topology_provider) 15 | {:ok, nodes} = topology_provider.mnesia_nodes(); 16 | nodes 17 | end 18 | 19 | #----------------------------------------------------------------------------- 20 | # ChangeSets 21 | #----------------------------------------------------------------------------- 22 | def change_sets do 23 | [ 24 | %ChangeSet{ 25 | changeset: "Supporting Tables for Default Implementation", 26 | author: "Keith Brings", 27 | note: "Test Data", 28 | environments: [:test, :dev], 29 | update: fn() -> 30 | neighbors = neighbors() 31 | create_table(Noizu.SimplePool.Database.DispatchTable, [memory: neighbors]) 32 | 33 | create_table(Noizu.SimplePool.Database.Dispatch.MonitorTable, [memory: neighbors]) 34 | 35 | create_table(Noizu.SimplePool.Database.MonitoringFramework.SettingTable, [memory: neighbors]) 36 | create_table(Noizu.SimplePool.Database.MonitoringFramework.NodeTable, [memory: neighbors]) 37 | create_table(Noizu.SimplePool.Database.MonitoringFramework.Node.EventTable, [memory: neighbors]) 38 | create_table(Noizu.SimplePool.Database.MonitoringFramework.ServiceTable, [memory: neighbors]) 39 | create_table(Noizu.SimplePool.Database.MonitoringFramework.Service.EventTable, [memory: neighbors]) 40 | create_table(Noizu.SimplePool.Database.MonitoringFramework.Service.HintTable, [memory: neighbors]) 41 | 42 | :success 43 | end, 44 | rollback: fn() -> 45 | destroy_table(Noizu.SimplePool.Database.DispatchTable) 46 | destroy_table(Noizu.SimplePool.Database.Dispatch.MonitorTable) 47 | 48 | destroy_table(Noizu.SimplePool.Database.MonitoringFramework.SettingTable) 49 | destroy_table(Noizu.SimplePool.Database.MonitoringFramework.NodeTable) 50 | destroy_table(Noizu.SimplePool.Database.MonitoringFramework.Node.EventTable) 51 | destroy_table(Noizu.SimplePool.Database.MonitoringFramework.ServiceTable) 52 | destroy_table(Noizu.SimplePool.Database.MonitoringFramework.Service.EventTable) 53 | destroy_table(Noizu.SimplePool.Database.MonitoringFramework.Service.HintTable) 54 | :removed 55 | end 56 | } 57 | ] 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/support/schema_provider.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.Support.SchemaProvider do 7 | use Amnesia 8 | @behaviour Noizu.MnesiaVersioning.SchemaBehaviour 9 | 10 | def neighbors() do 11 | [node()] 12 | end 13 | 14 | #----------------------------------------------------------------------------- 15 | # ChangeSets 16 | #----------------------------------------------------------------------------- 17 | def change_sets do 18 | Noizu.SimplePool.Support.Schema.Core.change_sets() 19 | end 20 | 21 | end # End Mix.Task.Migrate 22 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/support/topology_provider.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.Support.TopologyProvider do 7 | @behaviour Noizu.MnesiaVersioning.TopologyBehaviour 8 | 9 | def mnesia_nodes() do 10 | {:ok, [node()]} 11 | end 12 | 13 | def database() do 14 | [Noizu.SimplePool.Database] 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/worker/health_check.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.Worker.HealthCheck do 7 | @vsn 1.0 8 | @type t :: %__MODULE__{ 9 | identifier: any, 10 | status: :online | :degraged | :critical | :offline, 11 | event_frequency: Map.t, 12 | check: float, 13 | events: list, 14 | vsn: any 15 | } 16 | 17 | defstruct [ 18 | identifier: nil, 19 | status: :offline, 20 | event_frequency: %{}, 21 | check: 0.0, 22 | events: [], 23 | vsn: @vsn 24 | ] 25 | 26 | 27 | 28 | defimpl Inspect, for: Noizu.SimplePool.Worker.HealthCheck do 29 | import Inspect.Algebra 30 | def inspect(entity, opts) do 31 | heading = "#Worker.HealthCheck(#{inspect entity.identifier})" 32 | {seperator, end_seperator} = if opts.pretty, do: {"\n ", "\n"}, else: {" ", " "} 33 | inner = cond do 34 | opts.limit == :infinity -> 35 | concat(["<#{seperator}", to_doc(Map.from_struct(entity), opts), "#{end_seperator}>"]) 36 | opts.limit > 100 -> 37 | bare = %{status: entity.status} 38 | concat(["<#{seperator}", to_doc(bare, opts), "#{end_seperator}>"]) 39 | true -> "<>" 40 | end 41 | concat [heading, inner] 42 | end # end inspect/2 43 | end # end defimpl 44 | end 45 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/worker/link.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.Worker.Link do 7 | 8 | @type t :: %__MODULE__{ 9 | ref: any, 10 | handler: Module, 11 | handle: pid, 12 | expire: integer, 13 | update_after: integer | :infinity, 14 | state: :dead | :alive | {:error, any} | :invalid | :unknown 15 | } 16 | 17 | defstruct [ 18 | ref: nil, 19 | handler: nil, 20 | handle: nil, 21 | expire: 0, 22 | update_after: 300, # Default recheck links after 5 minutes, increase or reduce depending on how critical delivery of message is. or set to :infinity if other mechanism in place to update. 23 | state: :unknown 24 | ] 25 | 26 | 27 | defimpl Inspect, for: Noizu.SimplePool.Worker.Link do 28 | import Inspect.Algebra 29 | def inspect(entity, opts) do 30 | heading = "#Worker.Link(#{inspect entity.ref})" 31 | {seperator, end_seperator} = if opts.pretty, do: {"\n ", "\n"}, else: {" ", " "} 32 | inner = cond do 33 | opts.limit == :infinity -> 34 | concat(["<#{seperator}", to_doc(Map.from_struct(entity), opts), "#{end_seperator}>"]) 35 | opts.limit > 100 -> 36 | bare = %{handler: entity.handler, handle: entity.handle, expire: entity.expire, state: entity.state} 37 | concat(["<#{seperator}", to_doc(bare, opts), "#{end_seperator}>"]) 38 | opts.limit > 10 -> 39 | bare = %{handler: entity.handler, state: entity.state} 40 | concat(["<#{seperator}", to_doc(bare, opts), "#{end_seperator}>"]) 41 | true -> "<>" 42 | end 43 | concat [heading, inner] 44 | end # end inspect/2 45 | end # end defimpl 46 | end 47 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/worker/state.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.Worker.State do 7 | alias Noizu.SimplePool.Worker.State 8 | @type t :: %State{ 9 | initialized: boolean, 10 | migrating: boolean, 11 | worker_ref: tuple, 12 | inner_state: any, 13 | last_activity: any, 14 | extended: any 15 | } 16 | 17 | defstruct [ 18 | initialized: false, 19 | migrating: false, 20 | worker_ref: nil, 21 | inner_state: nil, 22 | last_activity: nil, 23 | extended: %{} 24 | ] 25 | 26 | defimpl Inspect, for: Noizu.SimplePool.Worker.State do 27 | import Inspect.Algebra 28 | def inspect(entity, opts) do 29 | heading = "#Worker.State(#{inspect entity.worker_ref})" 30 | {separator, end_separator} = if opts.pretty, do: {"\n ", "\n"}, else: {" ", " "} 31 | inner = cond do 32 | opts.limit == :infinity -> 33 | concat(["<#{separator}", to_doc(Map.from_struct(entity), opts), "#{end_separator}>"]) 34 | true -> "<>" 35 | end 36 | concat [heading, inner] 37 | end # end inspect/2 38 | end # end defimpl 39 | 40 | end 41 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/worker_lookup_behaviour/default.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.WorkerLookupBehaviour.Default do 7 | use Noizu.SimplePool.WorkerLookupBehaviour.DefaultImplementation 8 | end 9 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/worker_lookup_behaviour/default_implementation.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | defmodule Noizu.SimplePool.WorkerLookupBehaviour.DefaultImplementation do 6 | require Logger 7 | alias Noizu.ElixirCore.OptionSettings 8 | alias Noizu.ElixirCore.OptionValue 9 | alias Noizu.ElixirCore.OptionList 10 | 11 | @methods [:host!, :record_event!, :register!, :unregister!, :obtain_lock!, :release_lock!, :process!, :events!, :workers!, :set_node!] 12 | def prepare_options(options) do 13 | settings = %OptionSettings{ 14 | option_settings: %{ 15 | only: %OptionList{option: :only, default: @methods, valid_members: @methods, membership_set: true}, 16 | override: %OptionList{option: :override, default: [], valid_members: @methods, membership_set: true}, 17 | dispatch: %OptionValue{option: :dispatch, default: Application.get_env(:noizu_simple_pool, :default_dispatch, Noizu.SimplePool.DispatchRepo)}, 18 | dispatch_monitor: %OptionValue{option: :dispatch_monitor, default: Application.get_env(:noizu_simple_pool, :default_dispatch_monitor, Noizu.SimplePool.Dispatch.MonitorRepo)}, 19 | server_monitor: %OptionValue{option: :server_monitor, default: Application.get_env(:noizu_simple_pool, :default_server_monitor, Noizu.SimplePool.MonitoringFramework.MonitorBehaviour.Default)}, 20 | registry: %OptionValue{option: :registry, default: Application.get_env(:noizu_simple_pool, :default_registry, Noizu.SimplePool.DispatchRegister)}, 21 | } 22 | } 23 | initial = OptionSettings.expand(settings, options) 24 | modifications = Map.put(initial.effective_options, :required, List.foldl(@methods, %{}, fn(x, acc) -> Map.put(acc, x, initial.effective_options.only[x] && !initial.effective_options.override[x]) end)) 25 | %OptionSettings{initial| effective_options: Map.merge(initial.effective_options, modifications)} 26 | end 27 | 28 | defmacro __using__(options) do 29 | option_settings = prepare_options(options) 30 | options = option_settings.effective_options 31 | dispatch = options.dispatch 32 | dispatch_monitor = options.dispatch_monitor 33 | server_monitor = options.server_monitor 34 | registry = options.registry 35 | required = options.required 36 | 37 | quote do 38 | import unquote(__MODULE__) 39 | @behaviour Noizu.SimplePool.WorkerLookupBehaviour 40 | @dispatch unquote(dispatch) 41 | @dispatch_monitor unquote(dispatch_monitor) 42 | @server_monitor unquote(server_monitor) 43 | @registry unquote(registry) 44 | @pass_thru {__MODULE__, @dispatch, @dispatch_monitor, @server_monitor, @registry} 45 | 46 | if unquote(required.workers!) do 47 | def workers!(host, service_entity, context), do: Noizu.SimplePool.WorkerLookupBehaviourDefault.workers!(@pass_thru, host, service_entity, context) 48 | def workers!(host, service_entity, context, options), do: Noizu.SimplePool.WorkerLookupBehaviourDefault.workers!(@pass_thru, host, service_entity, context, options) 49 | end 50 | 51 | if unquote(required.host!) do 52 | def host!(ref, server, context), do: Noizu.SimplePool.WorkerLookupBehaviourDefault.host!(@pass_thru, ref, server.base(), server, context) 53 | def host!(ref, server, context, options), do: Noizu.SimplePool.WorkerLookupBehaviourDefault.host!(@pass_thru, ref, server.base(), server, context, options) 54 | end 55 | 56 | if unquote(required.record_event!) do 57 | def record_event!(ref, event, details, context), do: Noizu.SimplePool.WorkerLookupBehaviourDefault.record_event!(@pass_thru, ref, event, details, context) 58 | def record_event!(ref, event, details, context, options), do: Noizu.SimplePool.WorkerLookupBehaviourDefault.record_event!(@pass_thru, ref, event, details, context, options) 59 | end 60 | 61 | if unquote(required.events!) do 62 | def events!(ref, context), do: Noizu.SimplePool.WorkerLookupBehaviourDefault.events!(@pass_thru, ref, context) 63 | def events!(ref, context, options), do: Noizu.SimplePool.WorkerLookupBehaviourDefault.events!(@pass_thru, ref, context, options) 64 | end 65 | 66 | if unquote(required.set_node!) do 67 | def set_node!(ref, context), do: Noizu.SimplePool.WorkerLookupBehaviourDefault.set_node!(@pass_thru, ref, context) 68 | def set_node!(ref, context, options), do: Noizu.SimplePool.WorkerLookupBehaviourDefault.set_node!(@pass_thru, ref, context, options) 69 | end 70 | 71 | if unquote(required.register!) do 72 | def register!(ref, context), do: Noizu.SimplePool.WorkerLookupBehaviourDefault.register!(@pass_thru, ref, context) 73 | def register!(ref, context, options), do: Noizu.SimplePool.WorkerLookupBehaviourDefault.register!(@pass_thru, ref, context, options) 74 | end 75 | 76 | if unquote(required.unregister!) do 77 | def unregister!(ref, context), do: Noizu.SimplePool.WorkerLookupBehaviourDefault.unregister!(@pass_thru, ref, context) 78 | def unregister!(ref, context, options), do: Noizu.SimplePool.WorkerLookupBehaviourDefault.unregister!(@pass_thru, ref, context, options) 79 | end 80 | 81 | if unquote(required.obtain_lock!) do 82 | def obtain_lock!(ref, context), do: Noizu.SimplePool.WorkerLookupBehaviourDefault.obtain_lock!(@pass_thru, ref, context) 83 | def obtain_lock!(ref, context, options), do: Noizu.SimplePool.WorkerLookupBehaviourDefault.obtain_lock!(@pass_thru, ref, context, options) 84 | end 85 | 86 | if unquote(required.release_lock!) do 87 | def release_lock!(ref, context), do: Noizu.SimplePool.WorkerLookupBehaviourDefault.release_lock!(@pass_thru, ref, context) 88 | def release_lock!(ref, context, options), do: Noizu.SimplePool.WorkerLookupBehaviourDefault.release_lock!(@pass_thru, ref, context, options) 89 | end 90 | 91 | if unquote(required.process!) do 92 | def process!(ref, base, server, context), do: Noizu.SimplePool.WorkerLookupBehaviourDefault.process!(@pass_thru, ref, base, server, context) 93 | def process!(ref, base, server, context, options), do: Noizu.SimplePool.WorkerLookupBehaviourDefault.process!(@pass_thru, ref, base, server, context, options) 94 | end 95 | 96 | end 97 | end 98 | 99 | end -------------------------------------------------------------------------------- /lib/simple_pool/v1/worker_lookup_behaviour/dynamic.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.WorkerLookupBehaviour.Dynamic do 7 | use Noizu.SimplePool.WorkerLookupBehaviour.DefaultImplementation, 8 | server_monitor: Noizu.MonitoringFramework.EnvironmentPool.Server 9 | 10 | 11 | 12 | end 13 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/worker_lookup_behaviour/worker_lookup_behaviour.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.WorkerLookupBehaviour do 7 | @type lock_response :: {:ack, record :: any} | {:nack, {details :: any, record :: any}} | {:nack, details :: any} | {:error, details :: any} 8 | 9 | @callback workers!(any, any, any, any) :: {:ack, list} | any 10 | 11 | @callback host!(ref :: tuple, server :: module, Noizu.ElixirCore.CallingContext.t | nil, opts :: Map.t) :: {:ok, atom} | {:spawn, atom} | {:error, details :: any} | {:restricted, atom} 12 | @callback record_event!(ref :: tuple, event :: atom, details :: any, Noizu.ElixirCore.CallingContext.t | nil, opts :: Map.t) :: any 13 | @callback events!(ref :: tuple, Noizu.ElixirCore.CallingContext.t | nil, opts :: Map.t) :: list 14 | 15 | @callback register!(ref :: tuple, Noizu.ElixirCore.CallingContext.t | nil, opts :: Map.t) :: any 16 | @callback unregister!(ref :: tuple, Noizu.ElixirCore.CallingContext.t | nil, opts :: Map.t) :: any 17 | 18 | @callback process!(ref :: tuple, server :: module, Noizu.ElixirCore.CallingContext.t | nil, opts :: Map.t) :: lock_response 19 | @callback obtain_lock!(ref :: tuple, Noizu.ElixirCore.CallingContext.t | nil, opts :: Map.t) :: lock_response 20 | @callback release_lock!(ref :: tuple, Noizu.ElixirCore.CallingContext.t | nil, opts :: Map.t) :: lock_response 21 | 22 | end 23 | -------------------------------------------------------------------------------- /lib/simple_pool/v1/worker_supervisor_behaviour.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.WorkerSupervisorBehaviour do 7 | alias Noizu.ElixirCore.OptionSettings 8 | alias Noizu.ElixirCore.OptionValue 9 | alias Noizu.ElixirCore.OptionList 10 | require Logger 11 | @callback start_link() :: any 12 | @callback init(any) :: any 13 | 14 | @methods ([:start_link, :child, :init, :verbose, :options, :option_settings]) 15 | 16 | @default_max_seconds (5) 17 | @default_max_restarts (1000) 18 | @default_strategy (:one_for_one) 19 | 20 | def prepare_options(options) do 21 | settings = %OptionSettings{ 22 | option_settings: %{ 23 | only: %OptionList{option: :only, default: @methods, valid_members: @methods, membership_set: true}, 24 | override: %OptionList{option: :override, default: [], valid_members: @methods, membership_set: true}, 25 | verbose: %OptionValue{option: :verbose, default: :auto}, 26 | restart_type: %OptionValue{option: :restart_type, default: Application.get_env(:noizu_simple_pool, :pool_restart_type, :transient)}, 27 | max_restarts: %OptionValue{option: :max_restarts, default: Application.get_env(:noizu_simple_pool, :pool_max_restarts, @default_max_restarts)}, 28 | max_seconds: %OptionValue{option: :max_seconds, default: Application.get_env(:noizu_simple_pool, :pool_max_seconds, @default_max_seconds)}, 29 | strategy: %OptionValue{option: :strategy, default: Application.get_env(:noizu_simple_pool, :pool_strategy, @default_strategy)} 30 | } 31 | } 32 | 33 | initial = OptionSettings.expand(settings, options) 34 | modifications = Map.put(initial.effective_options, :required, List.foldl(@methods, %{}, fn(x, acc) -> Map.put(acc, x, initial.effective_options.only[x] && !initial.effective_options.override[x]) end)) 35 | %OptionSettings{initial| effective_options: Map.merge(initial.effective_options, modifications)} 36 | end 37 | 38 | 39 | def default_verbose(verbose, base) do 40 | if verbose == :auto do 41 | if Application.get_env(:noizu_simple_pool, base, %{})[:PoolSupervisor][:verbose] do 42 | Application.get_env(:noizu_simple_pool, base, %{})[:PoolSupervisor][:verbose] 43 | else 44 | Application.get_env(:noizu_simple_pool, :verbose, false) 45 | end 46 | else 47 | verbose 48 | end 49 | end 50 | 51 | defmacro __using__(options) do 52 | option_settings = prepare_options(options) 53 | options = option_settings.effective_options 54 | 55 | required = options.required 56 | max_restarts = options.max_restarts 57 | max_seconds = options.max_seconds 58 | strategy = options.strategy 59 | restart_type = options.restart_type 60 | verbose = options.verbose 61 | 62 | quote do 63 | #@behaviour Noizu.SimplePool.WorkerSupervisorBehaviour 64 | #@before_compile unquote(__MODULE__) 65 | @base Module.split(__MODULE__) |> Enum.slice(0..-2) |> Module.concat 66 | @worker Module.concat([@base, "Worker"]) 67 | use DynamicSupervisor 68 | import unquote(__MODULE__) 69 | require Logger 70 | 71 | @strategy unquote(strategy) 72 | @restart_type unquote(restart_type) 73 | @max_restarts unquote(max_restarts) 74 | @max_seconds unquote(max_seconds) 75 | 76 | @base_verbose (unquote(verbose)) 77 | @option_settings unquote(Macro.escape(option_settings)) 78 | @options unquote(Macro.escape(options)) 79 | 80 | if (unquote(required.verbose)) do 81 | def verbose(), do: default_verbose(@base_verbose, @base) 82 | end 83 | 84 | if (unquote(required.option_settings)) do 85 | def option_settings(), do: @option_settings 86 | end 87 | 88 | if (unquote(required.options)) do 89 | def options(), do: @options 90 | end 91 | 92 | # @start_link 93 | if (unquote(required.start_link)) do 94 | def start_link(definition, context) do 95 | if verbose() do 96 | Logger.info(fn -> {@base.banner("#{__MODULE__}.start_link"), Noizu.ElixirCore.CallingContext.metadata(context)} end) 97 | :skip 98 | end 99 | DynamicSupervisor.start_link(__MODULE__, [definition, context], [{:name, __MODULE__}]) 100 | end 101 | end # end start_link 102 | 103 | 104 | # @init 105 | if (unquote(required.init)) do 106 | def init([definition, context]) do 107 | if verbose() do 108 | Logger.info(fn -> {Noizu.SimplePool.Behaviour.banner("#{__MODULE__} INIT", "args: #{inspect context}"), Noizu.ElixirCore.CallingContext.metadata(context) } end) 109 | end 110 | DynamicSupervisor.init([{:strategy, @strategy}, {:max_restarts, @max_restarts}, {:max_seconds, @max_seconds}]) 111 | end 112 | end # end init 113 | 114 | @before_compile unquote(__MODULE__) 115 | end # end quote 116 | end #end __using__ 117 | 118 | defmacro __before_compile__(_env) do 119 | quote do 120 | 121 | def handle_call(:force_kill, _from, _state) do 122 | # For testing worker_supervisor recovery. 123 | throw "Crash WorkerSupervisor" 124 | end 125 | 126 | def handle_call(uncaught, _from, state) do 127 | Logger.info(fn -> "Uncaught handle_call to #{__MODULE__} . . . #{inspect uncaught}" end) 128 | {:noreply, state} 129 | end 130 | 131 | def handle_cast(uncaught, state) do 132 | Logger.info(fn -> "Uncaught handle_cast to #{__MODULE__} . . . #{inspect uncaught}" end) 133 | {:noreply, state} 134 | end 135 | 136 | def handle_info(uncaught, state) do 137 | Logger.info(fn -> "Uncaught handle_info to #{__MODULE__} . . . #{inspect uncaught}" end) 138 | {:noreply, state} 139 | end 140 | end # end quote 141 | end # end __before_compile__ 142 | 143 | end 144 | -------------------------------------------------------------------------------- /lib/simple_pool/v2/behaviours/service_management/service_management_provider.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2019 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | # @todo should be renamed Default and moved into behaviour definition for consistency. 7 | defmodule Noizu.SimplePool.V2.ServiceManagement.ServiceManagementProvider do 8 | alias Noizu.SimplePool.V2.Server.State, as: ServerState 9 | require Logger 10 | 11 | @doc """ 12 | 13 | """ 14 | def default_definition(pool_server) do 15 | pool_server.meta()[:default_definition] 16 | |> put_in([Access.key(:time_stamp)], DateTime.utc_now()) 17 | end 18 | 19 | @doc """ 20 | 21 | """ 22 | def enable_server!(_pool_server, _node), do: :pending # Not implemented for V1 either 23 | 24 | @doc """ 25 | 26 | """ 27 | def disable_server!(_pool_server, _node), do: :pending # Not implemented for V1 either 28 | 29 | @doc """ 30 | 31 | """ 32 | def status(pool_server, args \\ {}, context \\ nil), do: pool_server.router().internal_call({:status, args}, context) 33 | 34 | @doc """ 35 | 36 | """ 37 | def load_pool(pool_server, args \\ {}, context \\ nil, options \\ nil), do: pool_server.router().internal_system_call({:load_pool, args, options}, context) 38 | 39 | @doc """ 40 | 41 | """ 42 | def load_complete(_pool_server, %ServerState{} = this, _process, _context) do 43 | this 44 | |> put_in([Access.key(:status), Access.key(:loading)], :complete) 45 | |> put_in([Access.key(:status), Access.key(:state)], :ready) 46 | #|> put_in([Access.key(:environment_details), Access.key(:effective), Access.key(:status)], :online) 47 | #|> put_in([Access.key(:environment_details), Access.key(:effective), Access.key(:directive)], :online) 48 | |> put_in([Access.key(:extended), Access.key(:load_process)], nil) 49 | end 50 | 51 | @doc """ 52 | 53 | """ 54 | def load_begin(_pool_server, %ServerState{} = this, process, _context) do 55 | this 56 | |> put_in([Access.key(:status), Access.key(:loading)], :started) 57 | |> put_in([Access.key(:status), Access.key(:state)], :loading) 58 | #|> put_in([Access.key(:environment_details), Access.key(:effective), Access.key(:status)], :loading) 59 | #|> put_in([Access.key(:environment_details), Access.key(:effective), Access.key(:directive)], :loading) 60 | |> put_in([Access.key(:extended), Access.key(:load_process)], process) 61 | end 62 | 63 | 64 | @doc """ 65 | 66 | """ 67 | def status_wait(pool_server, target_state, context, timeout \\ :infinity) 68 | def status_wait(pool_server, target_state, context, timeout) when is_atom(target_state) do 69 | status_wait(pool_server, MapSet.new([target_state]), context, timeout) 70 | end 71 | 72 | def status_wait(pool_server, target_state, context, timeout) when is_list(target_state) do 73 | status_wait(pool_server, MapSet.new(target_state), context, timeout) 74 | end 75 | 76 | def status_wait(pool_server, %MapSet{} = target_state, context, timeout) do 77 | if timeout == :infinity do 78 | case pool_server.service_management().entity_status(context) do 79 | {:ack, state} -> if MapSet.member?(target_state, state), do: state, else: status_wait(pool_server, target_state, context, timeout) 80 | _ -> status_wait(pool_server, target_state, context, timeout) 81 | end 82 | else 83 | ts = :os.system_time(:millisecond) 84 | case pool_server.service_management().entity_status(context, %{timeout: timeout}) do 85 | {:ack, state} -> 86 | if MapSet.member?(target_state, state) do 87 | state 88 | else 89 | t = timeout - (:os.system_time(:millisecond) - ts) 90 | if t > 0 do 91 | status_wait(pool_server, target_state, context, t) 92 | else 93 | {:timeout, state} 94 | end 95 | end 96 | v -> 97 | t = timeout - (:os.system_time(:millisecond) - ts) 98 | if t > 0 do 99 | status_wait(pool_server, target_state, context, t) 100 | else 101 | {:timeout, v} 102 | end 103 | end 104 | end 105 | end 106 | 107 | @doc """ 108 | 109 | """ 110 | def entity_status(pool_server, context, options \\ %{}) do 111 | try do 112 | pool_server.router().internal_system_call({:status, {}, options}, context, options) 113 | catch 114 | :rescue, e -> 115 | case e do 116 | {:timeout, c} -> {:timeout, c} 117 | _ -> {:error, {:rescue, e}} 118 | end 119 | 120 | :exit, e -> 121 | case e do 122 | {:timeout, c} -> {:timeout, c} 123 | _ -> {:error, {:exit, e}} 124 | end 125 | end # end try 126 | end 127 | 128 | @doc """ 129 | 130 | """ 131 | def server_kill!(pool_server, args \\ {}, context \\ nil, options \\ %{}), do: pool_server.router().internal_cast({:server_kill!, args, options}, context, options) 132 | 133 | @doc """ 134 | 135 | """ 136 | def service_health_check!(pool_server, %Noizu.ElixirCore.CallingContext{} = context) do 137 | pool_server.router().internal_system_call({:health_check!, {}, %{}}, context) 138 | end 139 | 140 | def service_health_check!(pool_server, health_check_options, %Noizu.ElixirCore.CallingContext{} = context) do 141 | pool_server.router().internal_system_call({:health_check!, health_check_options, %{}}, context) 142 | end 143 | 144 | def service_health_check!(pool_server, health_check_options, %Noizu.ElixirCore.CallingContext{} = context, options) do 145 | pool_server.router().internal_system_call({:health_check!, health_check_options, options}, context, options) 146 | end 147 | 148 | 149 | @doc """ 150 | 151 | """ 152 | def record_service_event!(pool_server, event, details, context, _options) do 153 | Logger.error("Service Manager V2 record_service_event NYI| #{inspect pool_server}, #{inspect event}, #{inspect details}", Noizu.ElixirCore.CallingContext.metadata(context)) 154 | :ok 155 | end 156 | 157 | end 158 | -------------------------------------------------------------------------------- /lib/simple_pool/v2/behaviours/service_management_behaviour.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2019 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.ServiceManagementBehaviour do 7 | require Logger 8 | 9 | @callback default_definition() :: any 10 | @callback enable_server!(any) :: any 11 | @callback disable_server!(any) :: any 12 | @callback status(any) :: any 13 | @callback load_pool(any, any) :: any 14 | @callback load_complete(any, any, any) :: any 15 | @callback load_begin(any, any, any) :: any 16 | @callback status_wait(any, any, any) :: any 17 | @callback entity_status(any, any) :: any 18 | @callback server_kill!(any, any) :: any 19 | @callback service_health_check!(any) :: any 20 | @callback service_health_check!(any, any) :: any 21 | @callback service_health_check!(any, any, any) :: any 22 | @callback record_service_event!(any, any, any, any) :: any 23 | 24 | defmodule DefaultProvider do 25 | defmacro __using__(_options) do 26 | quote do 27 | require Logger 28 | @behaviour Noizu.SimplePool.V2.ServiceManagementBehaviour 29 | @pool_server Module.split(__MODULE__) |> Enum.slice(0..-2) |> Module.concat() 30 | alias Noizu.SimplePool.V2.Server.State, as: ServerState 31 | alias Noizu.SimplePool.Server.EnvironmentDetails 32 | alias Noizu.SimplePool.V2.ServiceManagement.ServiceManagementProvider, as: Provider 33 | 34 | @doc """ 35 | 36 | """ 37 | def default_definition(), do: Provider.default_definition(@pool_server) 38 | 39 | @doc """ 40 | 41 | """ 42 | def enable_server!(node), do: Provider.enable_server!(@pool_server, node) 43 | 44 | @doc """ 45 | 46 | """ 47 | def disable_server!(node), do: Provider.disable_server!(@pool_server, node) 48 | 49 | @doc """ 50 | 51 | """ 52 | def status(args \\ {}, context \\ nil), do: Provider.status(@pool_server, args, context) 53 | 54 | @doc """ 55 | 56 | """ 57 | def load_pool(args \\ {}, context \\ nil, options \\ nil), do: Provider.load_pool(@pool_server, args, context, options) 58 | 59 | @doc """ 60 | 61 | """ 62 | def load_complete(this, process, context), do: Provider.load_complete(@pool_server, this, process, context) 63 | 64 | @doc """ 65 | 66 | """ 67 | def load_begin(this, process, context), do: Provider.load_begin(@pool_server, this, process, context) 68 | 69 | @doc """ 70 | 71 | """ 72 | def status_wait(target_state, context, timeout \\ :infinity), do: Provider.status_wait(@pool_server, target_state, context, timeout) 73 | 74 | @doc """ 75 | 76 | """ 77 | def entity_status(context, options \\ %{}), do: Provider.entity_status(@pool_server, context, options) 78 | 79 | @doc """ 80 | 81 | """ 82 | def server_kill!(args \\ {}, context \\ nil, options \\ %{}), do: Provider.server_kill!(@pool_server, args, context, options) 83 | 84 | @doc """ 85 | 86 | """ 87 | def service_health_check!(%Noizu.ElixirCore.CallingContext{} = context), do: Provider.service_health_check!(@pool_server, context) 88 | def service_health_check!(health_check_options, %Noizu.ElixirCore.CallingContext{} = context), do: Provider.service_health_check!(@pool_server, health_check_options, context) 89 | def service_health_check!(health_check_options, %Noizu.ElixirCore.CallingContext{} = context, options), do: Provider.service_health_check!(@pool_server, health_check_options, context, options) 90 | 91 | @doc """ 92 | 93 | """ 94 | def record_service_event!(event, details, context, options), do: Provider.record_service_event!(@pool_server, event, details, context, options) 95 | 96 | defoverridable [ 97 | default_definition: 0, 98 | 99 | enable_server!: 1, 100 | disable_server!: 1, 101 | 102 | status: 1, 103 | 104 | load_pool: 2, 105 | load_complete: 3, 106 | load_begin: 3, 107 | 108 | status_wait: 3, 109 | 110 | entity_status: 2, 111 | 112 | server_kill!: 2, 113 | 114 | service_health_check!: 1, 115 | service_health_check!: 2, 116 | service_health_check!: 3, 117 | 118 | record_service_event!: 4, 119 | 120 | ] 121 | end 122 | end 123 | end 124 | 125 | 126 | end 127 | -------------------------------------------------------------------------------- /lib/simple_pool/v2/cluster_management_framework/structures/entities/cluster/definition.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2020 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.ClusterManagement.Cluster.Definition do 7 | @vsn 1.0 8 | @type t :: %__MODULE__{ 9 | cluster: any, 10 | monitors: Map.t, 11 | telemetrics: Map.t, 12 | meta: Map.t, 13 | vsn: any 14 | } 15 | 16 | defstruct [ 17 | cluster: nil, 18 | monitors: %{}, 19 | telemetrics: %{}, 20 | meta: %{}, 21 | vsn: @vsn 22 | ] 23 | 24 | def new(cluster) do 25 | %__MODULE__{ 26 | cluster: cluster, 27 | } 28 | end 29 | end -------------------------------------------------------------------------------- /lib/simple_pool/v2/cluster_management_framework/structures/entities/cluster/node/definition.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2020 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.ClusterManagement.Cluster.Node.Definition do 7 | @vsn 1.0 8 | @type t :: %__MODULE__{ 9 | node: any, 10 | worker_window: Map.t, 11 | ram_window: Map.t, 12 | cpu_window: Map.t, 13 | disk_window: Map.t, 14 | weight: float, 15 | monitors: Map.t, 16 | telemetrics: Map.t, 17 | meta: Map.t, 18 | vsn: any 19 | } 20 | 21 | defstruct [ 22 | node: nil, 23 | worker_window: %{low: nil, high: nil, target: nil}, 24 | ram_window: %{low: nil, high: nil, target: nil}, 25 | cpu_window: %{low: nil, high: nil, target: nil}, 26 | disk_window: %{low: nil, high: nil, target: nil}, 27 | weight: 1.0, 28 | monitors: %{}, 29 | telemetrics: %{}, 30 | meta: %{}, 31 | vsn: @vsn 32 | ] 33 | 34 | def new(node, worker_window, ram_window, cpu_window, disk_window, weight) do 35 | %__MODULE__{ 36 | node: node, 37 | worker_window: worker_window, 38 | ram_window: ram_window, 39 | cpu_window: cpu_window, 40 | disk_window: disk_window, 41 | weight: weight 42 | } 43 | end 44 | end -------------------------------------------------------------------------------- /lib/simple_pool/v2/cluster_management_framework/structures/entities/cluster/node/state_entity.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2020 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.ClusterManagement.Cluster.Node.StateEntity do 7 | @vsn 1.0 8 | @type t :: %__MODULE__{ 9 | identifier: atom, 10 | status: atom, 11 | state: atom, 12 | pending_state: atom, 13 | node_definition: Noizu.SimplePool.V2.ClusterManagement.Cluster.Node.Definition.t, 14 | status_details: Noizu.SimplePool.V2.ClusterManagement.Cluster.Node.Status.t, 15 | service_instances: Map.t, 16 | service_instances_statuses: Map.t, 17 | service_instances_health_reports: Map.t, 18 | health_report: Noizu.SimplePool.V2.ClusterManagement.HealthReport.t, 19 | updated_on: DateTime.t, 20 | state_changed_on: DateTime.t, 21 | pending_state_changed_on: DateTime.t, 22 | meta: Map.t, 23 | vsn: any 24 | } 25 | 26 | defstruct [ 27 | identifier: nil, 28 | status: nil, 29 | state: nil, 30 | node_definition: nil, 31 | status_details: nil, 32 | pending_state: nil, 33 | service_instances: %{}, 34 | service_instances_statuses: %{}, 35 | service_instances_health_reports: %{}, 36 | health_report: :pending, 37 | updated_on: nil, 38 | state_changed_on: nil, 39 | pending_state_changed_on: nil, 40 | meta: %{}, 41 | vsn: @vsn 42 | ] 43 | 44 | def reset(%__MODULE__{} = this, _context, options \\ %{}) do 45 | current_time = options[:current_time] || DateTime.utc_now() 46 | # @TODO flag service status entries as unknown/pending to force status updates. 47 | %__MODULE__{this| 48 | status: :warmup, 49 | state: :init, 50 | pending_state: :online, 51 | state_changed_on: current_time, 52 | pending_state_changed_on: current_time 53 | } 54 | end 55 | 56 | 57 | use Noizu.Scaffolding.V2.EntityBehaviour, 58 | sref_module: "node-state", 59 | entity_table: Noizu.SimplePool.V2.Database.Cluster.Node.StateTable 60 | 61 | defimpl Noizu.ERP, for: Noizu.SimplePool.V2.ClusterManagement.Cluster.Node.StateEntity do 62 | defdelegate id(o), to: Noizu.Scaffolding.V2.ERPResolver 63 | defdelegate ref(o), to: Noizu.Scaffolding.V2.ERPResolver 64 | defdelegate sref(o), to: Noizu.Scaffolding.V2.ERPResolver 65 | defdelegate entity(o, options \\ nil), to: Noizu.Scaffolding.V2.ERPResolver 66 | defdelegate entity!(o, options \\ nil), to: Noizu.Scaffolding.V2.ERPResolver 67 | defdelegate record(o, options \\ nil), to: Noizu.Scaffolding.V2.ERPResolver 68 | defdelegate record!(o, options \\ nil), to: Noizu.Scaffolding.V2.ERPResolver 69 | 70 | def id_ok(o) do 71 | r = id(o) 72 | r && {:ok, r} || {:error, o} 73 | end 74 | def ref_ok(o) do 75 | r = ref(o) 76 | r && {:ok, r} || {:error, o} 77 | end 78 | def sref_ok(o) do 79 | r = sref(o) 80 | r && {:ok, r} || {:error, o} 81 | end 82 | def entity_ok(o, options \\ %{}) do 83 | r = entity(o, options) 84 | r && {:ok, r} || {:error, o} 85 | end 86 | def entity_ok!(o, options \\ %{}) do 87 | r = entity!(o, options) 88 | r && {:ok, r} || {:error, o} 89 | end 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /lib/simple_pool/v2/cluster_management_framework/structures/entities/cluster/node/status.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2020 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.ClusterManagement.Cluster.Node.Status do 7 | @vsn 1.0 8 | @type t :: %__MODULE__{ 9 | node: any, 10 | status: atom, 11 | state: atom, 12 | desired_state: atom, 13 | health_report: Noizu.SimplePool.V2.ClusterManagement.HealthReport.t, 14 | updated_on: DateTime.t, 15 | state_changed_on: DateTime.t, 16 | desired_state_changed_on: DateTime.t, 17 | meta: Map.t, 18 | vsn: any 19 | } 20 | 21 | defstruct [ 22 | node: nil, 23 | status: :unknown, 24 | state: :offline, 25 | desired_state: :offline, 26 | health_report: nil, 27 | updated_on: nil, 28 | state_changed_on: nil, 29 | desired_state_changed_on: nil, 30 | meta: %{}, 31 | vsn: @vsn 32 | ] 33 | 34 | def new(node) do 35 | %__MODULE__{ 36 | node: node, 37 | } 38 | end 39 | 40 | end -------------------------------------------------------------------------------- /lib/simple_pool/v2/cluster_management_framework/structures/entities/cluster/service/definition.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2020 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.ClusterManagement.Cluster.Service.Definition do 7 | @vsn 1.0 8 | @type t :: %__MODULE__{ 9 | service: any, 10 | worker_window: Map.t, 11 | node_window: Map.t, 12 | monitors: Map.t, 13 | telemetrics: Map.t, 14 | meta: Map.t, 15 | vsn: any 16 | } 17 | 18 | defstruct [ 19 | service: nil, 20 | worker_window: %{low: nil, high: nil, target: nil}, 21 | node_window: %{low: nil, high: nil, target: nil}, 22 | monitors: %{}, 23 | telemetrics: %{}, 24 | meta: %{}, 25 | vsn: @vsn 26 | ] 27 | 28 | def new(service, worker_window, node_window) do 29 | %__MODULE__{ 30 | service: service, 31 | worker_window: worker_window, 32 | node_window: node_window, 33 | } 34 | end 35 | end -------------------------------------------------------------------------------- /lib/simple_pool/v2/cluster_management_framework/structures/entities/cluster/service/instance/definition.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2020 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.ClusterManagement.Cluster.Service.Instance.Definition do 7 | @vsn 1.0 8 | @type t :: %__MODULE__{ 9 | service: any, 10 | node: any, 11 | launch_parameters: Map.t, 12 | worker_window: Map.t, 13 | weight: float, 14 | monitors: Map.t, 15 | telemetrics: Map.t, 16 | meta: Map.t, 17 | vsn: any 18 | } 19 | 20 | defstruct [ 21 | service: nil, 22 | node: nil, 23 | launch_parameters: :auto, 24 | worker_window: %{low: nil, high: nil, target: nil}, 25 | weight: 1.0, 26 | monitors: %{}, 27 | telemetrics: %{}, 28 | meta: %{}, 29 | vsn: @vsn 30 | ] 31 | 32 | def new(service, node, worker_window, weight) do 33 | %__MODULE__{ 34 | service: service, 35 | node: node, 36 | worker_window: worker_window, 37 | weight: weight 38 | } 39 | end 40 | end -------------------------------------------------------------------------------- /lib/simple_pool/v2/cluster_management_framework/structures/entities/cluster/service/instance/state_entity.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2020 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.ClusterManagement.Cluster.Service.Instance.StateEntity do 7 | @vsn 1.0 8 | @type t :: %__MODULE__{ 9 | identifier: atom, # {node, service} 10 | status: atom, 11 | state: atom, 12 | pending_state: atom, 13 | service_instance_definition: Noizu.SimplePool.V2.ClusterManagement.Cluster.Service.Instance.Definition.t, 14 | status_details: Noizu.SimplePool.V2.ClusterManagement.Cluster.Service.Instance.Status.t, 15 | health_report: Noizu.SimplePool.V2.ClusterManagement.HealthReport.t, 16 | updated_on: DateTime.t, 17 | telemetry_handler: any, 18 | event_handler: any, 19 | state_changed_on: DateTime.t, 20 | pending_state_changed_on: DateTime.t, 21 | meta: Map.t, 22 | vsn: any 23 | } 24 | 25 | defstruct [ 26 | identifier: nil, 27 | status: :unknown, 28 | state: :offline, 29 | pending_state: :offline, 30 | service_instance_definition: nil, 31 | status_details: nil, 32 | health_report: :pending, 33 | updated_on: nil, 34 | telemetry_handler: nil, 35 | event_handler: nil, 36 | state_changed_on: nil, 37 | pending_state_changed_on: nil, 38 | meta: %{}, 39 | vsn: @vsn 40 | ] 41 | 42 | def reset(%__MODULE__{} = this, _context, options \\ %{}) do 43 | current_time = options[:current_time] || DateTime.utc_now() 44 | # @TODO flag service status entries as unknown/pending to force status updates. 45 | %__MODULE__{this| 46 | status: :warmup, 47 | state: :init, 48 | pending_state: :online, 49 | state_changed_on: current_time, 50 | pending_state_changed_on: current_time 51 | } 52 | end 53 | 54 | use Noizu.Scaffolding.V2.EntityBehaviour, 55 | sref_module: "service-instance-state", 56 | entity_table: Noizu.SimplePool.V2.Database.Cluster.Service.Instance.StateTable 57 | 58 | defimpl Noizu.ERP, for: Noizu.SimplePool.V2.ClusterManagement.Cluster.Service.Instance.StateEntity do 59 | defdelegate id(o), to: Noizu.Scaffolding.V2.ERPResolver 60 | defdelegate ref(o), to: Noizu.Scaffolding.V2.ERPResolver 61 | defdelegate sref(o), to: Noizu.Scaffolding.V2.ERPResolver 62 | defdelegate entity(o, options \\ nil), to: Noizu.Scaffolding.V2.ERPResolver 63 | defdelegate entity!(o, options \\ nil), to: Noizu.Scaffolding.V2.ERPResolver 64 | defdelegate record(o, options \\ nil), to: Noizu.Scaffolding.V2.ERPResolver 65 | defdelegate record!(o, options \\ nil), to: Noizu.Scaffolding.V2.ERPResolver 66 | 67 | def id_ok(o) do 68 | r = id(o) 69 | r && {:ok, r} || {:error, o} 70 | end 71 | def ref_ok(o) do 72 | r = ref(o) 73 | r && {:ok, r} || {:error, o} 74 | end 75 | def sref_ok(o) do 76 | r = sref(o) 77 | r && {:ok, r} || {:error, o} 78 | end 79 | def entity_ok(o, options \\ %{}) do 80 | r = entity(o, options) 81 | r && {:ok, r} || {:error, o} 82 | end 83 | def entity_ok!(o, options \\ %{}) do 84 | r = entity!(o, options) 85 | r && {:ok, r} || {:error, o} 86 | end 87 | end 88 | end 89 | -------------------------------------------------------------------------------- /lib/simple_pool/v2/cluster_management_framework/structures/entities/cluster/service/instance/status.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2020 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.ClusterManagement.Cluster.Service.Instance.Status do 7 | @vsn 1.0 8 | @type t :: %__MODULE__{ 9 | service: any, 10 | node: any, 11 | service_monitor: any, 12 | service_process: any, 13 | service_error: any, 14 | status: atom, 15 | state: atom, 16 | pending_state: atom, 17 | health_report: Noizu.SimplePool.V2.ClusterManagement.HealthReport.t, 18 | updated_on: DateTime.t, 19 | state_changed_on: DateTime.t, 20 | pending_state_changed_on: DateTime.t, 21 | meta: Map.t, 22 | vsn: any 23 | } 24 | 25 | defstruct [ 26 | service: nil, 27 | node: nil, 28 | service_monitor: nil, 29 | service_process: nil, 30 | service_error: nil, 31 | status: :unknown, 32 | state: :offline, 33 | pending_state: :offline, 34 | health_report: nil, 35 | updated_on: nil, 36 | state_changed_on: nil, 37 | pending_state_changed_on: nil, 38 | meta: %{}, 39 | vsn: @vsn 40 | ] 41 | 42 | def new(service, node, pool_supervisor_pid, error) do 43 | %__MODULE__{ 44 | service: service, 45 | node: node, 46 | service_monitor: pool_supervisor_pid && Process.monitor(pool_supervisor_pid), 47 | service_process: pool_supervisor_pid, 48 | service_error: error, 49 | updated_on: DateTime.utc_now(), 50 | state_changed_on: DateTime.utc_now(), 51 | pending_state_changed_on: DateTime.utc_now(), 52 | } 53 | end 54 | 55 | end -------------------------------------------------------------------------------- /lib/simple_pool/v2/cluster_management_framework/structures/entities/cluster/service/state_entity.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2020 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.ClusterManagement.Cluster.Service.StateEntity do 7 | @vsn 1.0 8 | @type t :: %__MODULE__{ 9 | identifier: atom, 10 | status: atom, 11 | state: atom, 12 | pending_state: atom, 13 | service_definition: Noizu.SimplePool.V2.ClusterManagement.Cluster.Service.Definition.t, 14 | status_details: Noizu.SimplePool.V2.ClusterManagement.Cluster.Service.Status.t, 15 | instance_definitions: Map.t, 16 | instance_statuses: Map.t, 17 | health_report: Noizu.SimplePool.V2.ClusterManagement.HealthReport.t, 18 | updated_on: DateTime.t, 19 | telemetry_handler: any, 20 | event_handler: any, 21 | state_changed_on: DateTime.t, 22 | pending_state_changed_on: DateTime.t, 23 | meta: Map.t, 24 | vsn: any 25 | } 26 | 27 | defstruct [ 28 | identifier: nil, 29 | status: :unknown, 30 | state: :offline, 31 | pending_state: :offline, 32 | service_definition: nil, 33 | status_details: nil, 34 | instance_definitions: %{}, 35 | instance_statuses: %{}, 36 | health_report: :pending, 37 | updated_on: nil, 38 | telemetry_handler: nil, 39 | event_handler: nil, 40 | state_changed_on: nil, 41 | pending_state_changed_on: nil, 42 | meta: %{}, 43 | vsn: @vsn 44 | ] 45 | 46 | def reset(%__MODULE__{} = this, _context, options \\ %{}) do 47 | current_time = options[:current_time] || DateTime.utc_now() 48 | # @TODO flag service status entries as unknown/pending to force status updates. 49 | %__MODULE__{this| 50 | status: :warmup, 51 | state: :init, 52 | pending_state: :online, 53 | state_changed_on: current_time, 54 | pending_state_changed_on: current_time 55 | } 56 | end 57 | 58 | use Noizu.Scaffolding.V2.EntityBehaviour, 59 | sref_module: "service-state", 60 | entity_table: Noizu.SimplePool.V2.Database.Cluster.Service.StateTable 61 | 62 | defimpl Noizu.ERP, for: Noizu.SimplePool.V2.ClusterManagement.Cluster.Service.StateEntity do 63 | defdelegate id(o), to: Noizu.Scaffolding.V2.ERPResolver 64 | defdelegate ref(o), to: Noizu.Scaffolding.V2.ERPResolver 65 | defdelegate sref(o), to: Noizu.Scaffolding.V2.ERPResolver 66 | defdelegate entity(o, options \\ nil), to: Noizu.Scaffolding.V2.ERPResolver 67 | defdelegate entity!(o, options \\ nil), to: Noizu.Scaffolding.V2.ERPResolver 68 | defdelegate record(o, options \\ nil), to: Noizu.Scaffolding.V2.ERPResolver 69 | defdelegate record!(o, options \\ nil), to: Noizu.Scaffolding.V2.ERPResolver 70 | 71 | def id_ok(o) do 72 | r = id(o) 73 | r && {:ok, r} || {:error, o} 74 | end 75 | def ref_ok(o) do 76 | r = ref(o) 77 | r && {:ok, r} || {:error, o} 78 | end 79 | def sref_ok(o) do 80 | r = sref(o) 81 | r && {:ok, r} || {:error, o} 82 | end 83 | def entity_ok(o, options \\ %{}) do 84 | r = entity(o, options) 85 | r && {:ok, r} || {:error, o} 86 | end 87 | def entity_ok!(o, options \\ %{}) do 88 | r = entity!(o, options) 89 | r && {:ok, r} || {:error, o} 90 | end 91 | end 92 | end 93 | -------------------------------------------------------------------------------- /lib/simple_pool/v2/cluster_management_framework/structures/entities/cluster/service/status.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2020 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.ClusterManagement.Cluster.Service.Status do 7 | @vsn 1.0 8 | @type t :: %__MODULE__{ 9 | service: any, 10 | instances: Map.t, 11 | status: atom, 12 | state: atom, 13 | desired_state: atom, 14 | health_report: Noizu.SimplePool.V2.ClusterManagement.HealthReport.t, 15 | updated_on: DateTime.t, 16 | state_changed_on: DateTime.t, 17 | desired_state_changed_on: DateTime.t, 18 | meta: Map.t, 19 | vsn: any 20 | } 21 | 22 | defstruct [ 23 | service: nil, 24 | instances: %{}, 25 | status: :unknown, 26 | state: :offline, 27 | desired_state: :offline, 28 | health_report: nil, 29 | updated_on: nil, 30 | state_changed_on: nil, 31 | desired_state_changed_on: nil, 32 | meta: %{}, 33 | vsn: @vsn 34 | ] 35 | end -------------------------------------------------------------------------------- /lib/simple_pool/v2/cluster_management_framework/structures/entities/cluster/state_entity.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2020 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.ClusterManagement.Cluster.StateEntity do 7 | @vsn 1.0 8 | @type t :: %__MODULE__{ 9 | identifier: atom, 10 | node: atom, 11 | process: pid, 12 | status: atom, 13 | state: atom, 14 | pending_state: atom, 15 | cluster_definition: Noizu.SimplePool.V2.ClusterManagement.Cluster.Definition.t, 16 | service_definitions: Map.t, 17 | service_statuses: Map.t, 18 | status_details: Map.t, 19 | health_report: Noizu.SimplePool.V2.ClusterManagement.HealthReport.t, 20 | updated_on: DateTime.t, 21 | telemetry_handler: any, 22 | event_handler: any, 23 | state_changed_on: DateTime.t, 24 | pending_state_changed_on: DateTime.t, 25 | meta: Map.t, 26 | vsn: any 27 | } 28 | 29 | defstruct [ 30 | identifier: nil, 31 | node: nil, 32 | process: nil, 33 | status: :unknown, 34 | state: :offline, 35 | pending_state: :offline, 36 | cluster_definition: nil, 37 | service_definitions: %{}, 38 | service_statuses: %{}, 39 | status_details: %{}, 40 | health_report: :pending, 41 | updated_on: nil, 42 | telemetry_handler: nil, 43 | event_handler: nil, 44 | state_changed_on: nil, 45 | pending_state_changed_on: nil, 46 | meta: %{}, 47 | vsn: @vsn 48 | ] 49 | 50 | def reset(%__MODULE__{} = this, _context, options \\ %{}) do 51 | # Todo flag statuses as pending 52 | current_time = options[:current_time] || DateTime.utc_now() 53 | %__MODULE__{this| 54 | node: options[:node] || node(), 55 | process: options[:pid] || self(), 56 | status: :warmup, 57 | state: :init, 58 | pending_state: :online, 59 | state_changed_on: current_time, 60 | pending_state_changed_on: current_time, 61 | #state_changed_on: DateTime.utc_now(), 62 | #pending_state_changed_on: DateTime.utc_now() 63 | } 64 | end 65 | 66 | use Noizu.Scaffolding.V2.EntityBehaviour, 67 | sref_module: "cluster-state", 68 | entity_table: Noizu.SimplePool.V2.Database.Cluster.StateTable 69 | 70 | defimpl Noizu.ERP, for: Noizu.SimplePool.V2.ClusterManagement.Cluster.StateEntity do 71 | defdelegate id(o), to: Noizu.Scaffolding.V2.ERPResolver 72 | defdelegate ref(o), to: Noizu.Scaffolding.V2.ERPResolver 73 | defdelegate sref(o), to: Noizu.Scaffolding.V2.ERPResolver 74 | defdelegate entity(o, options \\ nil), to: Noizu.Scaffolding.V2.ERPResolver 75 | defdelegate entity!(o, options \\ nil), to: Noizu.Scaffolding.V2.ERPResolver 76 | defdelegate record(o, options \\ nil), to: Noizu.Scaffolding.V2.ERPResolver 77 | defdelegate record!(o, options \\ nil), to: Noizu.Scaffolding.V2.ERPResolver 78 | 79 | def id_ok(o) do 80 | r = id(o) 81 | r && {:ok, r} || {:error, o} 82 | end 83 | def ref_ok(o) do 84 | r = ref(o) 85 | r && {:ok, r} || {:error, o} 86 | end 87 | def sref_ok(o) do 88 | r = sref(o) 89 | r && {:ok, r} || {:error, o} 90 | end 91 | def entity_ok(o, options \\ %{}) do 92 | r = entity(o, options) 93 | r && {:ok, r} || {:error, o} 94 | end 95 | def entity_ok!(o, options \\ %{}) do 96 | r = entity!(o, options) 97 | r && {:ok, r} || {:error, o} 98 | end 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /lib/simple_pool/v2/cluster_management_framework/structures/entities/cluster/status.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2020 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.ClusterManagement.Cluster.Status do 7 | @vsn 1.0 8 | @type t :: %__MODULE__{ 9 | cluster: any, 10 | status: atom, 11 | state: atom, 12 | desired_state: atom, 13 | health_report: Noizu.SimplePool.V2.ClusterManagement.HealthReport.t, 14 | updated_on: DateTime.t, 15 | state_changed_on: DateTime.t, 16 | desired_state_changed_on: DateTime.t, 17 | meta: Map.t, 18 | vsn: any 19 | } 20 | 21 | defstruct [ 22 | cluster: nil, 23 | status: :unknown, 24 | state: :offline, 25 | desired_state: :offline, 26 | health_report: nil, 27 | updated_on: nil, 28 | state_changed_on: nil, 29 | desired_state_changed_on: nil, 30 | meta: %{}, 31 | vsn: @vsn 32 | ] 33 | 34 | def new(cluster) do 35 | %__MODULE__{ 36 | cluster: cluster, 37 | } 38 | end 39 | end -------------------------------------------------------------------------------- /lib/simple_pool/v2/cluster_management_framework/structures/entities/health_report.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2020 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | defmodule Noizu.SimplePool.V2.ClusterManagement.HealthReport do 6 | @vsn 1.0 7 | @type t :: %__MODULE__{ 8 | subject: any, 9 | summary: any, 10 | checks: Map.t, 11 | updated_on: DateTime.t, 12 | meta: Map.t, 13 | vsn: any 14 | } 15 | 16 | defstruct [ 17 | subject: nil, 18 | summary: :pending, 19 | checks: %{}, 20 | updated_on: nil, 21 | meta: %{}, 22 | vsn: @vsn 23 | ] 24 | 25 | def new(subject) do 26 | %__MODULE__{ 27 | subject: subject 28 | } 29 | end 30 | 31 | end -------------------------------------------------------------------------------- /lib/simple_pool/v2/cluster_management_framework/structures/entities/health_report/check.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2020 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.ClusterManagement.HealthReport.Check do 7 | @vsn 1.0 8 | @type t :: %__MODULE__{ 9 | name: any, 10 | provider: any, 11 | summary: any, 12 | details: Map.t, 13 | updated_on: DateTime.t, 14 | meta: Map.t, 15 | vsn: any 16 | } 17 | 18 | defstruct [ 19 | name: nil, 20 | provider: nil, 21 | summary: :pending, 22 | details: nil, 23 | updated_on: nil, 24 | meta: %{}, 25 | vsn: @vsn 26 | ] 27 | end -------------------------------------------------------------------------------- /lib/simple_pool/v2/cluster_management_framework/structures/entities/health_report_definition.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2020 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.ClusterManagement.HealthReportDefinition do 7 | @vsn 1.0 8 | @type t :: %__MODULE__{ 9 | subject: any, 10 | checks: Map.t, 11 | updated_on: DateTime.t, 12 | meta: Map.t, 13 | vsn: any 14 | } 15 | 16 | defstruct [ 17 | subject: nil, 18 | checks: %{}, 19 | updated_on: nil, 20 | meta: %{}, 21 | vsn: @vsn 22 | ] 23 | 24 | def new(subject) do 25 | %__MODULE__{ 26 | subject: subject 27 | } 28 | end 29 | 30 | end -------------------------------------------------------------------------------- /lib/simple_pool/v2/cluster_management_framework/structures/repos/cluster/node/state_repo.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.ClusterManagement.Cluster.Node.StateRepo do 7 | use Noizu.Scaffolding.V2.RepoBehaviour, 8 | entity_table: Noizu.SimplePool.V2.Database.Cluster.Node.StateTable 9 | 10 | end -------------------------------------------------------------------------------- /lib/simple_pool/v2/cluster_management_framework/structures/repos/cluster/service/state_repo.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | defmodule Noizu.SimplePool.V2.ClusterManagement.Cluster.Service.StateRepo do 6 | use Noizu.Scaffolding.V2.RepoBehaviour, 7 | entity_table: Noizu.SimplePool.V2.Database.Cluster.Service.StateTable 8 | 9 | end -------------------------------------------------------------------------------- /lib/simple_pool/v2/cluster_management_framework/structures/repos/cluster/state_repo.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.ClusterManagement.Cluster.StateRepo do 7 | use Noizu.Scaffolding.V2.RepoBehaviour, 8 | entity_table: Noizu.SimplePool.V2.Database.Cluster.StateTable 9 | 10 | end -------------------------------------------------------------------------------- /lib/simple_pool/v2/dispatch_entity.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.DispatchEntity do 7 | @type t :: %__MODULE__{ 8 | identifier: any, 9 | state: atom, 10 | server: atom, # elixir_node 11 | lock: nil | {{atom, pid}, atom, integer} 12 | } 13 | 14 | defstruct [ 15 | identifier: nil, 16 | state: :spawning, 17 | server: nil, # elixir_node 18 | lock: nil 19 | ] 20 | end 21 | -------------------------------------------------------------------------------- /lib/simple_pool/v2/monitor_behaviour.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2019 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.MonitorBehaviour do 7 | @callback health_check(any, any) :: any 8 | @callback record_service_event!(any, any, any, any) :: any 9 | @callback lock!(any, any) :: any 10 | @callback release!(any, any) :: any 11 | 12 | defmodule Default do 13 | alias Noizu.ElixirCore.OptionSettings 14 | alias Noizu.ElixirCore.OptionValue 15 | alias Noizu.ElixirCore.OptionList 16 | require Logger 17 | 18 | # @todo alternative solution for specifying features. 19 | @features ([:auto_identifier, :lazy_load, :async_load, :inactivity_check, :s_redirect, :s_redirect_handle, :ref_lookup_cache, :call_forwarding, :graceful_stop, :crash_protection]) 20 | @default_features ([:lazy_load, :s_redirect, :s_redirect_handle, :inactivity_check, :call_forwarding, :graceful_stop, :crash_protection]) 21 | 22 | @default_timeout 15_000 23 | @default_shutdown_timeout 30_000 24 | 25 | def prepare_options(options) do 26 | settings = %OptionSettings{ 27 | option_settings: %{ 28 | features: %OptionList{option: :features, default: Application.get_env(:noizu_simple_pool, :default_features, @default_features), valid_members: @features, membership_set: false}, 29 | verbose: %OptionValue{option: :verbose, default: :auto}, 30 | worker_state_entity: %OptionValue{option: :worker_state_entity, default: :auto}, 31 | default_timeout: %OptionValue{option: :default_timeout, default: Application.get_env(:noizu_simple_pool, :default_timeout, @default_timeout)}, 32 | shutdown_timeout: %OptionValue{option: :shutdown_timeout, default: Application.get_env(:noizu_simple_pool, :default_shutdown_timeout, @default_shutdown_timeout)}, 33 | default_definition: %OptionValue{option: :default_definition, default: :auto}, 34 | log_timeouts: %OptionValue{option: :log_timeouts, default: Application.get_env(:noizu_simple_pool, :default_log_timeouts, true)}, 35 | max_supervisors: %OptionValue{option: :max_supervisors, default: Application.get_env(:noizu_simple_pool, :default_max_supervisors, 100)}, 36 | } 37 | } 38 | OptionSettings.expand(settings, options) 39 | end 40 | 41 | 42 | @temporary_core_events MapSet.new([:start, :shutdown]) 43 | def core_events(_pool) do 44 | # TODO use fast global wrapper around SettingTable 45 | @temporary_core_events 46 | end 47 | 48 | def record_service_event!(pool, event, _details, context, _opts) do 49 | if MapSet.member?(core_events(pool), event) do 50 | Logger.info(fn() -> {"TODO - write to ServiceEventTable #{inspect event}", Noizu.ElixirCore.CallingContext.metadata(context)} end) 51 | else 52 | Logger.info(fn() -> {"TODO - write to DetailedServiceEventTable #{inspect event}", Noizu.ElixirCore.CallingContext.metadata(context)} end) 53 | end 54 | end 55 | end 56 | 57 | defmacro __using__(options) do 58 | implementation = Keyword.get(options || [], :implementation, Noizu.SimplePool.V2.ServerBehaviour.Default) 59 | option_settings = implementation.prepare_options(options) 60 | 61 | # Temporary Hardcoding 62 | message_processing_provider = Noizu.SimplePool.V2.MessageProcessingBehaviour.DefaultProvider 63 | 64 | quote do 65 | use GenServer 66 | @pool :pending 67 | use Noizu.SimplePool.V2.SettingsBehaviour.Inherited, unquote([option_settings: option_settings]) 68 | use unquote(message_processing_provider), unquote(option_settings) 69 | 70 | #--------------- 71 | # start_link 72 | #--------------- 73 | def start_link(server_process \\ :error, definition \\ :default, context \\ nil) do 74 | # @todo :wip 75 | GenServer.start_link(__MODULE__, [server_process, definition, context], name: __MODULE__, restart: :permanent) 76 | end 77 | 78 | #--------------- 79 | # init 80 | #--------------- 81 | def init([server_process, definition, context] = args) do 82 | # @todo :wip 83 | {:ok, %{}} 84 | end 85 | 86 | def terminate(reason, state) do 87 | # @todo :wip 88 | :ok 89 | end 90 | 91 | def health_check(context, opts \\ %{}), do: :wip 92 | def record_service_event!(event, details, context, opts \\ %{}) do 93 | Noizu.SimplePool.V2.MonitorBehaviour.Default.record_service_event!(@pool, event, details, context, opts) 94 | end 95 | 96 | def lock!(context, opts \\ %{}), do: :wip 97 | def release!(context, opts \\ %{}), do: :wip 98 | 99 | 100 | #=============================================================================================================== 101 | # Overridable 102 | #=============================================================================================================== 103 | defoverridable [ 104 | start_link: 3, 105 | init: 1, 106 | terminate: 2, 107 | health_check: 2, 108 | record_service_event!: 4, 109 | lock!: 2, 110 | release!: 2 111 | ] 112 | end 113 | end 114 | 115 | end 116 | -------------------------------------------------------------------------------- /lib/simple_pool/v2/record_keeper_behaviour.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2019 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.RecordKeeperBehaviour do 7 | 8 | defmacro __using__(options) do 9 | implementation = Keyword.get(options || [], :implementation, Noizu.SimplePool.V2.ServerBehaviour.Default) 10 | option_settings = implementation.prepare_options(options) 11 | 12 | quote do 13 | use Agent 14 | 15 | @pool :pending 16 | @pool_worker_state_entity :pending 17 | use Noizu.SimplePool.V2.SettingsBehaviour.Inherited, unquote([option_settings: option_settings]) 18 | 19 | def initial_value(worker, args, context) do 20 | %{} 21 | end 22 | 23 | def start_link(worker, args, context) do 24 | Agent.start_link(fn -> initial_value(worker, args, context) end, name: agent(worker)) 25 | end 26 | 27 | def agent(worker) do 28 | ref = @pool_worker_state_entity.ref(worker) 29 | {__MODULE__, ref} 30 | end 31 | 32 | def increment(worker, record) do 33 | 34 | Agent.get_and_update(agent(worker), 35 | fn(state) -> 36 | state = update_in(state, [record], &( (&1 || 0) + 1)) 37 | {state[record], state} 38 | end 39 | ) 40 | end 41 | 42 | def decrement(worker, record) do 43 | Agent.get_and_update(agent(worker), 44 | fn(state) -> 45 | state = update_in(state, [record], &( (&1 || 0) - 1)) 46 | {state[record], state} 47 | end 48 | ) 49 | end 50 | 51 | def get(worker, record) do 52 | Agent.get(agent(worker), 53 | fn(state) -> 54 | state[record] 55 | end 56 | ) 57 | end 58 | 59 | def set(worker, record, value) do 60 | Agent.update(agent(worker), 61 | fn(state) -> 62 | put_in(state, [record], value) 63 | end 64 | ) 65 | end 66 | 67 | def get_state(worker) do 68 | Agent.get(agent(worker), 69 | fn(state) -> 70 | state 71 | end 72 | ) 73 | end 74 | 75 | def set_state(worker, records) do 76 | Agent.update(agent(worker), 77 | fn(state) -> 78 | records 79 | end 80 | ) 81 | end 82 | 83 | #=============================================================================================================== 84 | # Overridable 85 | #=============================================================================================================== 86 | defoverridable [ 87 | initial_value: 3, 88 | start_link: 3, 89 | increment: 2, 90 | decrement: 2, 91 | get: 2, 92 | set: 3, 93 | get_state: 1, 94 | set_state: 2, 95 | ] 96 | end 97 | end 98 | 99 | end 100 | -------------------------------------------------------------------------------- /lib/simple_pool/v2/server/state.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.Server.State do 7 | alias Noizu.SimplePool.V2.Server.State 8 | 9 | @type t :: %State{ 10 | worker_supervisor: any, # deprecated 11 | service: any, # deprecated 12 | pool: any, 13 | status_details: any, 14 | status: Map.t, 15 | extended: any, 16 | entity: any, 17 | environment_details: Noizu.SimplePool.Server.EnvironmentDetails.t, 18 | options: Noizu.SimplePool.OptionSettings.t, 19 | meta: Map.t, 20 | } 21 | 22 | defstruct [ 23 | worker_supervisor: nil, # deprecated 24 | service: nil, # deprecated 25 | 26 | pool: nil, 27 | status_details: nil, 28 | status: %{loading: :pending, state: :pending}, 29 | extended: %{}, 30 | entity: nil, 31 | environment_details: nil, 32 | options: nil, 33 | meta: %{}, 34 | ] 35 | 36 | #----------------------------------------------------------------------------- 37 | # Inspect Protocol 38 | #----------------------------------------------------------------------------- 39 | defimpl Inspect, for: Noizu.SimplePool.V2.Server.State do 40 | import Inspect.Algebra 41 | def inspect(entity, opts) do 42 | heading = "#Server.State(#{inspect entity.service})" 43 | {seperator, end_seperator} = if opts.pretty, do: {"\n ", "\n"}, else: {" ", " "} 44 | inner = cond do 45 | opts.limit == :infinity -> 46 | concat(["<#{seperator}", to_doc(Map.from_struct(entity), opts), "#{end_seperator}>"]) 47 | true -> "<>" 48 | end 49 | concat [heading, inner] 50 | end # end inspect/2 51 | end # end defimpl 52 | 53 | end 54 | -------------------------------------------------------------------------------- /lib/simple_pool/v2/stand_alone_service_behaviour.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.StandAloneServiceBehaviour do 7 | @moduledoc """ 8 | The Noizu.SimplePool.V2.Behaviour provides the entry point for Worker Pools. 9 | The developer will define a pool such as ChatRoomPool that uses the Noizu.SimplePool.V2.Behaviour Implementation 10 | before going on to define worker and server implementations. 11 | 12 | The module is relatively straight forward, it provides methods to get pool information (pool worker, pool supervisor) 13 | compile options, runtime settings (via the FastGlobal library and our meta function). 14 | """ 15 | 16 | defmacro __using__(options) do 17 | implementation = Keyword.get(options || [], :implementation, Noizu.SimplePool.V2.PoolBehaviour.Default) 18 | option_settings = implementation.prepare_options(Macro.expand(options, __CALLER__)) 19 | 20 | # Set stand alone flag. 21 | option_settings = option_settings 22 | |> put_in([Access.key(:effective_options), :stand_alone], true) 23 | |> put_in([Access.key(:effective_options), :monitor_options, :stand_alone], true) 24 | |> put_in([Access.key(:effective_options), :worker_options, :stand_alone], true) 25 | |> put_in([Access.key(:effective_options), :server_options, :stand_alone], true) 26 | |> put_in([Access.key(:effective_options), :worker_supervisor_options, :stand_alone], true) 27 | |> put_in([Access.key(:effective_options), :pool_supervisor_options, :stand_alone], true) 28 | 29 | options = option_settings.effective_options 30 | 31 | default_modules = options.default_modules 32 | message_processing_provider = Noizu.SimplePool.V2.MessageProcessingBehaviour.DefaultProvider 33 | 34 | quote do 35 | require Logger 36 | @behaviour Noizu.SimplePool.V2.PoolBehaviour 37 | @implementation unquote(implementation) 38 | @module __MODULE__ 39 | 40 | use Noizu.SimplePool.V2.SettingsBehaviour.Base, unquote([option_settings: option_settings, stand_alone: true]) 41 | use unquote(message_processing_provider), unquote(option_settings) 42 | 43 | #-------------------------- 44 | # Methods 45 | #-------------------------- 46 | defdelegate start(definition \\ :default, context \\ nil), to: __MODULE__.PoolSupervisor, as: :start_link 47 | def stand_alone(), do: true 48 | 49 | 50 | #-------------- Routing Delegates ------------------- 51 | # Convenience methods should be placed at the pool level, 52 | # which will in turn hook into the Server.Router and worker spawning logic 53 | # to delivery commands to the correct worker processes. 54 | #---------------------------------------------------- 55 | defdelegate s_call(identifier, call, context, options \\ nil, timeout \\ nil), to: __MODULE__.Server.Router 56 | defdelegate s_call!(identifier, call, context, options \\ nil, timeout \\ nil), to: __MODULE__.Server.Router 57 | defdelegate s_cast(identifier, call, context, options \\ nil), to: __MODULE__.Server.Router 58 | defdelegate s_cast!(identifier, call, context, options \\ nil), to: __MODULE__.Server.Router 59 | defdelegate get_direct_link!(ref, context, options \\ nil), to: __MODULE__.Server.Router 60 | defdelegate link_forward!(link, call, context, options \\ nil), to: __MODULE__.Server.Router 61 | defdelegate server_call(call, context \\ nil, options \\ nil), to: __MODULE__.Server.Router, as: :self_call 62 | defdelegate server_cast(call, context \\ nil, options \\ nil), to: __MODULE__.Server.Router, as: :self_cast 63 | defdelegate server_internal_call(call, context \\ nil, options \\ nil), to: __MODULE__.Server.Router, as: :internal_call 64 | defdelegate server_internal_cast(call, context \\ nil, options \\ nil), to: __MODULE__.Server.Router, as: :internal_cast 65 | defdelegate remote_server_internal_call(remote_node, call, context \\ nil, options \\ nil), to: __MODULE__.Server.Router, as: :remote_call 66 | defdelegate remote_server_internal_cast(remote_node, call, context \\ nil, options \\ nil), to: __MODULE__.Server.Router, as: :remote_cast 67 | defdelegate server_system_call(call, context \\ nil, options \\ nil), to: __MODULE__.Server.Router, as: :internal_system_call 68 | defdelegate server_system_cast(call, context \\ nil, options \\ nil), to: __MODULE__.Server.Router, as: :internal_system_cast 69 | defdelegate remote_server_system_call(elixir_node, call, context \\ nil, options \\ nil), to: __MODULE__.Server.Router, as: :remote_system_call 70 | defdelegate remote_server_system_cast(elixir_node, call, context \\ nil, options \\ nil), to: __MODULE__.Server.Router, as: :remote_system_cast 71 | 72 | 73 | 74 | #-------------------------- 75 | # Overridable 76 | #-------------------------- 77 | defoverridable [ 78 | start: 2, 79 | stand_alone: 0, 80 | 81 | 82 | #----------------------------- 83 | # Routing Overrides 84 | #----------------------------- 85 | s_call: 5, 86 | s_call!: 5, 87 | s_cast: 4, 88 | s_cast!: 4, 89 | get_direct_link!: 3, 90 | link_forward!: 4, 91 | server_call: 3, 92 | server_cast: 3, 93 | server_internal_call: 3, 94 | server_internal_cast: 3, 95 | remote_server_internal_call: 4, 96 | remote_server_internal_cast: 4, 97 | server_system_call: 3, 98 | server_system_cast: 3, 99 | remote_server_system_call: 4, 100 | remote_server_system_cast: 4, 101 | 102 | ] 103 | 104 | #----------------------------------------- 105 | # Sub Modules 106 | #----------------------------------------- 107 | 108 | # Note, no WorkerSupervisor as this is a stand alone service with no children. 109 | 110 | if (unquote(default_modules.pool_supervisor)) do 111 | defmodule PoolSupervisor do 112 | use Noizu.SimplePool.V2.PoolSupervisorBehaviour, unquote(options.pool_supervisor_options) 113 | end 114 | end 115 | 116 | if (unquote(default_modules.monitor)) do 117 | defmodule Monitor do 118 | use Noizu.SimplePool.V2.MonitorBehaviour, unquote(options.monitor_options) 119 | end 120 | end 121 | 122 | # Note user must implement Server sub module. 123 | 124 | end # end quote 125 | end #end __using__ 126 | end 127 | -------------------------------------------------------------------------------- /lib/simple_pool/v2/support/database.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2019 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | use Amnesia 7 | 8 | defdatabase Noizu.SimplePool.V2.Database do 9 | 10 | #======================================================= 11 | # Cluster Management Tables 12 | #======================================================= 13 | deftable Cluster.SettingTable, [:setting, :value], type: :bag, index: [] do 14 | @type t :: %Cluster.SettingTable{setting: atom, value: any} 15 | end 16 | 17 | 18 | #----------------------------- 19 | # Cluster Manager 20 | #----------------------------- 21 | deftable Cluster.StateTable, [:identifier, :entity], type: :set, index: [] do 22 | @moduledoc """ 23 | Cluster Wide Configuration 24 | """ 25 | @type t :: %Cluster.StateTable{identifier: any, entity: any} 26 | end 27 | 28 | deftable Cluster.TaskTable, [:identifier, :entity], type: :set, index: [] do 29 | @moduledoc """ 30 | Pending Tasks Scheduled or Pending Approval (Rebalance Cluster, Shutdown Node, Select new Service Manager etc.) 31 | """ 32 | @type t :: %Cluster.TaskTable{identifier: any, entity: any} 33 | end 34 | 35 | 36 | #----------------------------- 37 | # Service Manager 38 | #----------------------------- 39 | deftable Cluster.Service.StateTable, [:identifier, :entity], type: :set, index: [] do 40 | @moduledoc """ 41 | Service State Snapshot, Active Manger Node, etc. 42 | """ 43 | @type t :: %Cluster.Service.StateTable{identifier: any, entity: any} 44 | end 45 | 46 | deftable Cluster.Service.WorkerTable, [:identifier, :entity], type: :set, index: [] do 47 | @moduledoc """ 48 | Service State Snapshot, Active Manger Node, etc. 49 | """ 50 | @type t :: %Cluster.Service.WorkerTable{identifier: any, entity: any} 51 | end 52 | 53 | deftable Cluster.Service.TaskTable, [:identifier, :entity], type: :set, index: [] do 54 | @moduledoc """ 55 | Pending Tasks Scheduled or Pending Approval. 56 | """ 57 | @type t :: %Cluster.Service.TaskTable{identifier: any, entity: any} 58 | end 59 | 60 | deftable Cluster.Service.Instance.StateTable, [:identifier, :entity], type: :set, index: [] do 61 | @moduledoc """ 62 | Per-Node Service State Snapshot 63 | """ 64 | @type t :: %Cluster.Service.Instance.StateTable{identifier: any, entity: any} 65 | end 66 | 67 | #----------------------------- 68 | # Node Manager 69 | #----------------------------- 70 | deftable Cluster.Node.StateTable, [:identifier, :entity], type: :set, index: [] do 71 | @type t :: %Cluster.Node.StateTable{identifier: any, entity: any} 72 | end 73 | 74 | deftable Cluster.Node.WorkerTable, [:identifier, :entity], type: :set, index: [] do 75 | @type t :: %Cluster.Node.WorkerTable{identifier: any, entity: any} 76 | end 77 | 78 | deftable Cluster.Node.TaskTable, [:identifier, :entity], type: :set, index: [] do 79 | @type t :: %Cluster.Node.TaskTable{identifier: any, entity: any} 80 | end 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | #==================================================================== 89 | # Deprecated 90 | #==================================================================== 91 | 92 | 93 | #-------------------------------------- 94 | # Monitoring Framework 95 | #-------------------------------------- 96 | deftable MonitoringFramework.SettingTable, [:setting, :value], type: :bag, index: [] do 97 | @type t :: %MonitoringFramework.SettingTable{setting: atom, value: any} 98 | end 99 | 100 | deftable MonitoringFramework.ConfigurationTable, [:identifier, :entity], type: :set, index: [] do 101 | @type t :: %MonitoringFramework.ConfigurationTable{identifier: any, entity: any} 102 | end 103 | 104 | deftable MonitoringFramework.NodeTable, [:identifier, :status, :directive, :health_index, :entity], type: :set, index: [] do 105 | @type t :: %MonitoringFramework.NodeTable{identifier: any, status: atom, directive: atom, health_index: float, entity: any} 106 | end 107 | 108 | deftable MonitoringFramework.ServiceTable, [:identifier, :status, :directive, :health_index, :entity], type: :set, index: [] do 109 | @type t :: %MonitoringFramework.ServiceTable{identifier: {atom, atom}, status: atom, directive: atom, health_index: float, entity: any} 110 | end 111 | 112 | deftable MonitoringFramework.DetailedServiceEventTable, [:identifier, :event, :time_stamp, :entity], local: true, type: :bag, index: [] do 113 | @type t :: %MonitoringFramework.DetailedServiceEventTable{identifier: {atom, atom}, event: atom, time_stamp: integer, entity: any} 114 | end 115 | 116 | deftable MonitoringFramework.ServiceEventTable, [:identifier, :event, :time_stamp, :entity], type: :bag, index: [] do 117 | @type t :: %MonitoringFramework.ServiceEventTable{identifier: {atom, atom}, event: atom, time_stamp: integer, entity: any} 118 | end 119 | 120 | deftable MonitoringFramework.ServerEventTable, [:identifier, :event, :time_stamp, :entity], type: :bag, index: [] do 121 | @type t :: %MonitoringFramework.ServerEventTable{identifier: atom, event: atom, time_stamp: integer, entity: any} 122 | end 123 | 124 | deftable MonitoringFramework.DetailedServerEventTable, [:identifier, :event, :time_stamp, :entity], type: :bag, local: true, index: [] do 125 | @type t :: %MonitoringFramework.DetailedServerEventTable{identifier: atom, event: atom, time_stamp: integer, entity: any} 126 | end 127 | 128 | deftable MonitoringFramework.ClusterEventTable, [:identifier, :event, :time_stamp, :entity], type: :bag, index: [] do 129 | @type t :: %MonitoringFramework.ClusterEventTable{identifier: atom, event: atom, time_stamp: integer, entity: any} 130 | end 131 | 132 | end -------------------------------------------------------------------------------- /lib/simple_pool/v2/support/schema/core.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2019 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.Support.Schema.Core do 7 | alias Noizu.MnesiaVersioning.ChangeSet 8 | use Amnesia 9 | use Noizu.SimplePool.V2.Database 10 | use Noizu.MnesiaVersioning.SchemaBehaviour 11 | 12 | def neighbors() do 13 | topology_provider = Application.get_env(:noizu_mnesia_versioning, :topology_provider) 14 | {:ok, nodes} = topology_provider.mnesia_nodes(); 15 | nodes 16 | end 17 | 18 | #----------------------------------------------------------------------------- 19 | # ChangeSets 20 | #----------------------------------------------------------------------------- 21 | def change_sets do 22 | [ 23 | %ChangeSet{ 24 | changeset: "Supporting Tables for Default Implementation", 25 | author: "Keith Brings", 26 | note: "SimplePool V2 Core Tables", 27 | environments: [:test, :dev], 28 | update: fn() -> 29 | neighbors = neighbors() 30 | create_table(Database.MonitoringFramework.SettingTable, [disk: neighbors]) 31 | create_table(Database.MonitoringFramework.ConfigurationTable, [disk: neighbors]) 32 | create_table(Database.MonitoringFramework.NodeTable, [disk: neighbors]) 33 | create_table(Database.MonitoringFramework.ServiceTable, [disk: neighbors]) 34 | 35 | create_table(Database.MonitoringFramework.DetailedServiceEventTable, [disk: neighbors]) 36 | create_table(Database.MonitoringFramework.ServiceEventTable, [disk: neighbors]) 37 | create_table(Database.MonitoringFramework.DetailedServerEventTable, [disk: neighbors]) 38 | create_table(Database.MonitoringFramework.ServerEventTable, [disk: neighbors]) 39 | create_table(Database.MonitoringFramework.ClusterEventTable, [disk: neighbors]) 40 | :success 41 | end, 42 | rollback: fn() -> 43 | destroy_table(Database.MonitoringFramework.SettingTable) 44 | destroy_table(Database.MonitoringFramework.ConfigurationTable) 45 | destroy_table(Database.MonitoringFramework.NodeTable) 46 | destroy_table(Database.MonitoringFramework.ServiceTable) 47 | 48 | destroy_table(Database.MonitoringFramework.DetailedServiceEventTable) 49 | destroy_table(Database.MonitoringFramework.ServiceEventTable) 50 | destroy_table(Database.MonitoringFramework.DetailedServerEventTable) 51 | destroy_table(Database.MonitoringFramework.ServerEventTable) 52 | destroy_table(Database.MonitoringFramework.ClusterEventTable) 53 | :removed 54 | end 55 | } 56 | ] 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/simple_pool/v2/support/schema_provider.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2019 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.Support.SchemaProvider do 7 | use Amnesia 8 | @behaviour Noizu.MnesiaVersioning.SchemaBehaviour 9 | 10 | def neighbors() do 11 | [node()] 12 | end 13 | 14 | #----------------------------------------------------------------------------- 15 | # ChangeSets 16 | #----------------------------------------------------------------------------- 17 | def change_sets do 18 | Noizu.SimplePool.V2.Support.Schema.Core.change_sets() 19 | end 20 | 21 | end # End Mix.Task.Migrate 22 | -------------------------------------------------------------------------------- /lib/simple_pool/v2/support/topology_provider.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2019 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.Support.TopologyProvider do 7 | @behaviour Noizu.MnesiaVersioning.TopologyBehaviour 8 | 9 | def mnesia_nodes() do 10 | {:ok, [node()]} 11 | end 12 | 13 | def database() do 14 | [Noizu.SimplePool.V2.Database] 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/simple_pool/v2/worker_supervisor/layer2_behaviour.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.WorkerSupervisor.Layer2Behaviour do 7 | @moduledoc """ 8 | WorkerSupervisorBehaviour provides the logic for managing a pool of workers. The top level Pool Supervisors will generally 9 | contain a number of WorkerSupervisors that in turn are referenced by Pool.Server to access, kill and spawn worker processes. 10 | 11 | @todo increase level of OTP nesting and hide some of the communication complexity from Pool.Server 12 | """ 13 | 14 | require Logger 15 | @callback start_link(any, any) :: any 16 | 17 | @callback child(any, any) :: any 18 | @callback child(any, any, any) :: any 19 | @callback child(any, any, any, any) :: any 20 | 21 | defmodule Default do 22 | @moduledoc """ 23 | Provides the default implementation for WorkerSupervisor modules. 24 | Using the strategy pattern here allows us to move logic out of the WorkerSupervisorBehaviour implementation 25 | which reduces the amount of generated code, and improve compile times. It additionally allows for developers to provide 26 | their own alternative implementations. 27 | """ 28 | alias Noizu.ElixirCore.OptionSettings 29 | alias Noizu.ElixirCore.OptionValue 30 | #alias Noizu.ElixirCore.OptionList 31 | 32 | require Logger 33 | 34 | @default_max_seconds (5) 35 | @default_max_restarts (1000) 36 | @default_strategy (:one_for_one) 37 | 38 | def prepare_options(options) do 39 | settings = %OptionSettings{ 40 | option_settings: %{ 41 | verbose: %OptionValue{option: :verbose, default: :auto}, 42 | restart_type: %OptionValue{option: :restart_type, default: Application.get_env(:noizu_simple_pool, :pool_restart_type, :transient)}, 43 | max_restarts: %OptionValue{option: :max_restarts, default: Application.get_env(:noizu_simple_pool, :pool_max_restarts, @default_max_restarts)}, 44 | max_seconds: %OptionValue{option: :max_seconds, default: Application.get_env(:noizu_simple_pool, :pool_max_seconds, @default_max_seconds)}, 45 | strategy: %OptionValue{option: :strategy, default: Application.get_env(:noizu_simple_pool, :pool_strategy, @default_strategy)} 46 | } 47 | } 48 | 49 | OptionSettings.expand(settings, options) 50 | end 51 | end 52 | 53 | defmacro __using__(options) do 54 | implementation = Keyword.get(options || [], :implementation, Noizu.SimplePool.V2.WorkerSupervisor.Layer2Behaviour.Default) 55 | option_settings = implementation.prepare_options(options) 56 | _options = option_settings.effective_options 57 | #@TODO - use real options. 58 | message_processing_provider = Noizu.SimplePool.V2.MessageProcessingBehaviour.DefaultProvider 59 | 60 | quote do 61 | @behaviour Noizu.SimplePool.V2.WorkerSupervisor.Layer2Behaviour 62 | use Supervisor 63 | require Logger 64 | 65 | @implementation unquote(implementation) 66 | 67 | #---------------------------------------- 68 | @options :override 69 | @option_settings :override 70 | use Noizu.SimplePool.V2.SettingsBehaviour.Inherited, unquote([option_settings: option_settings, depth: 2]) 71 | use unquote(message_processing_provider), unquote(option_settings) 72 | #---------------------------------------- 73 | 74 | #----------- 75 | # 76 | #----------- 77 | @doc """ 78 | 79 | """ 80 | def child(ref, context) do 81 | %{ 82 | id: ref, 83 | start: {pool_worker(), :start_link, [ref, context]}, 84 | restart: @options.restart_type, 85 | } 86 | end 87 | 88 | @doc """ 89 | 90 | """ 91 | def child(ref, params, context) do 92 | %{ 93 | id: ref, 94 | start: {pool_worker(), :start_link, [ref, params, context]}, 95 | restart: @options.restart_type, 96 | } 97 | end 98 | 99 | @doc """ 100 | 101 | """ 102 | def child(ref, params, context, options) do 103 | restart = options[:restart] || @options.restart_type 104 | %{ 105 | id: ref, 106 | start: {pool_worker(), :start_link, [ref, params, context]}, 107 | restart: restart, 108 | } 109 | end 110 | 111 | #----------- 112 | # 113 | #----------- 114 | @doc """ 115 | 116 | """ 117 | def start_link(definition, context) do 118 | verbose() && Logger.info(fn -> {banner("#{__MODULE__}.start_link"), Noizu.ElixirCore.CallingContext.metadata(context)} end) 119 | Supervisor.start_link(__MODULE__, [definition, context], [{:name, __MODULE__}]) 120 | end 121 | 122 | #----------- 123 | # 124 | #----------- 125 | @doc """ 126 | 127 | """ 128 | def init([_definition, context]) do 129 | verbose() && Logger.info(fn -> {banner("#{__MODULE__} INIT", "args: #{inspect context}"), Noizu.ElixirCore.CallingContext.metadata(context) } end) 130 | Supervisor.init([], [{:strategy, @options.strategy}, {:max_restarts, @options.max_restarts}, {:max_seconds, @options.max_seconds}]) 131 | end 132 | 133 | defoverridable [ 134 | start_link: 2, 135 | child: 2, 136 | child: 3, 137 | child: 4, 138 | init: 1, 139 | ] 140 | 141 | end # end quote 142 | end #end __using__ 143 | end 144 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.Mixfile do 7 | use Mix.Project 8 | 9 | def project do 10 | [app: :noizu_simple_pool, 11 | version: "2.2.9", 12 | elixir: "~> 1.13", 13 | package: package(), 14 | deps: deps(), 15 | description: "Noizu Simple Pool", 16 | docs: docs(), 17 | elixirc_paths: elixirc_paths(Mix.env), 18 | ] 19 | end # end project 20 | 21 | defp package do 22 | [ 23 | maintainers: ["noizu"], 24 | licenses: ["MIT"], 25 | links: %{"GitHub" => "https://github.com/noizu/SimplePool"} 26 | ] 27 | end # end package 28 | 29 | def application do 30 | [ 31 | applications: [:logger], 32 | extra_applications: [:amnesia, :noizu_mnesia_versioning, :noizu_scaffolding, :noizu_core, :fastglobal, :semaphore] 33 | 34 | ] 35 | end # end application 36 | 37 | defp deps do 38 | [ 39 | {:ex_doc, "~> 0.28.3", only: [:dev, :test], optional: true}, # Documentation Provider 40 | {:markdown, github: "devinus/markdown", only: [:dev], optional: true}, # Markdown processor for ex_doc 41 | {:amnesia, git: "https://github.com/noizu/amnesia.git", ref: "9266002"}, # Mnesia Wrapper 42 | {:noizu_scaffolding, github: "noizu/ElixirScaffolding", tag: "1.2.6"}, 43 | {:fastglobal, "~> 1.0"}, # https://github.com/discordapp/fastglobal 44 | {:semaphore, "~> 1.0"}, # https://github.com/discordapp/semaphore 45 | {:redix, github: "whatyouhide/redix", tag: "v0.7.0", optional: true}, 46 | {:poison, "~> 3.1.0", optional: true}, 47 | {:elixir_uuid, "~> 1.2", only: :test, optional: true}, 48 | {:plug, "~> 1.11.1", optional: true}, 49 | ] 50 | end # end deps 51 | 52 | defp docs do 53 | [ 54 | source_url_pattern: "https://github.com/noizu/SimplePool/blob/master/%{path}#L%{line}", 55 | extras: ["README.md"] 56 | ] 57 | end # end docs 58 | 59 | defp elixirc_paths(:test), do: ["lib","test/support"] 60 | defp elixirc_paths(_), do: ["lib"] 61 | 62 | end 63 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "amnesia": {:git, "https://github.com/noizu/amnesia.git", "9266002467face51b956a0c5f27954b145c0fb9d", [ref: "9266002"]}, 3 | "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, 4 | "earmark_parser": {:hex, :earmark_parser, "1.4.25", "2024618731c55ebfcc5439d756852ec4e85978a39d0d58593763924d9a15916f", [:mix], [], "hexpm", "56749c5e1c59447f7b7a23ddb235e4b3defe276afc220a6227237f3efe83f51e"}, 5 | "elixir_uuid": {:hex, :elixir_uuid, "1.2.1", "dce506597acb7e6b0daeaff52ff6a9043f5919a4c3315abb4143f0b00378c097", [:mix], [], "hexpm", "f7eba2ea6c3555cea09706492716b0d87397b88946e6380898c2889d68585752"}, 6 | "ex_doc": {:hex, :ex_doc, "0.28.3", "6eea2f69995f5fba94cd6dd398df369fe4e777a47cd887714a0976930615c9e6", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "05387a6a2655b5f9820f3f627450ed20b4325c25977b2ee69bed90af6688e718"}, 7 | "exquisite": {:git, "https://github.com/noizu/exquisite.git", "61d48f8c4eeb90ab0cafe9a4d98bdb2820db295d", [ref: "61d48f8"]}, 8 | "fastglobal": {:hex, :fastglobal, "1.0.0", "f3133a0cda8e9408aac7281ec579c4b4a8386ce0e99ca55f746b9f58192f455b", [:mix], [], "hexpm", "cfdb7ed63910bc75f579cd09e2517618fa9418b56731d51d03f7ba4b400798d0"}, 9 | "hoedown": {:git, "https://github.com/hoedown/hoedown.git", "980b9c549b4348d50b683ecee6abee470b98acda", []}, 10 | "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, 11 | "makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"}, 12 | "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, 13 | "markdown": {:git, "https://github.com/devinus/markdown.git", "d065dbcc4e242a85ca2516fdadd0082712871fd8", []}, 14 | "mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"}, 15 | "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, 16 | "noizu_core": {:git, "https://github.com/noizu/ElixirCore.git", "e1f17c041f7d655c6de528449773ff65389327d9", [tag: "1.0.13"]}, 17 | "noizu_mnesia_versioning": {:git, "https://github.com/noizu/MnesiaVersioning.git", "876860c51eeec464cd65cca265e50ff0fe2326db", [tag: "0.1.10"]}, 18 | "noizu_scaffolding": {:git, "https://github.com/noizu/ElixirScaffolding.git", "53caff68bb19a603f2903c69e49229e82eb7f29d", [tag: "1.2.6"]}, 19 | "plug": {:hex, :plug, "1.11.1", "f2992bac66fdae679453c9e86134a4201f6f43a687d8ff1cd1b2862d53c80259", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "23524e4fefbb587c11f0833b3910bfb414bf2e2534d61928e920f54e3a1b881f"}, 20 | "plug_crypto": {:hex, :plug_crypto, "1.2.2", "05654514ac717ff3a1843204b424477d9e60c143406aa94daf2274fdd280794d", [:mix], [], "hexpm", "87631c7ad914a5a445f0a3809f99b079113ae4ed4b867348dd9eec288cecb6db"}, 21 | "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm", "fec8660eb7733ee4117b85f55799fd3833eb769a6df71ccf8903e8dc5447cfce"}, 22 | "redix": {:git, "https://github.com/whatyouhide/redix.git", "5b2edd731e19e4703973d3b780f52df71223806f", [tag: "v0.7.0"]}, 23 | "semaphore": {:hex, :semaphore, "1.1.0", "435e20bf121da8b18faf5327540818050e9102723804c490e06c2f6d24b2a5a4", [:mix], [], "hexpm", "29b48df988af9ac3833a82d4350e5ce41a5944ad898607b8121131771fb5bdbc"}, 24 | "telemetry": {:hex, :telemetry, "0.4.3", "a06428a514bdbc63293cd9a6263aad00ddeb66f608163bdec7c8995784080818", [:rebar3], [], "hexpm", "eb72b8365ffda5bed68a620d1da88525e326cb82a75ee61354fc24b844768041"}, 25 | } 26 | -------------------------------------------------------------------------------- /run-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 3 | 4 | MIX_ENV=test iex --no-halt --name first@127.0.0.1 --cookie apple -S mix test 5 | 6 | 7 | -------------------------------------------------------------------------------- /test-node.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 3 | 4 | MIX_ENV=test iex --no-halt --name second@127.0.0.1 --cookie apple -S mix second_test_node 5 | 6 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | Test Notes 2 | ---------------------------------------- 3 | 4 | Because Simple pool deals with distributed system calls to running 5 | the acceptance test suite requires that more than one node is started. 6 | 7 | To launch the second node invoke `./test-node.sh` 8 | this will start a node names second as follows: 9 | ``` 10 | MIX_ENV=test iex --no-halt --name second@127.0.0.1 --cookie apple -S mix second_test_node 11 | ``` 12 | 13 | After launching the test node in a different terminal window run `./run-test.sh` or 14 | if you wish to specify specific suites `MIX_ENV=test iex --no-halt --name first@127.0.0.1 --cookie apple -S mix test --only v2` 15 | After tests run you will remain in the instance and may run additional manual tests or inspect pool state. 16 | 17 | You will need to restart both nodes every time you re-run tests. 18 | run-test.sh will stall during setup if tests have already run against the node started by test-node.sh 19 | 20 | Happy Testing! -------------------------------------------------------------------------------- /test/simple_pool/v1/event_entry_tests.exs.disabled: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V1.EventEntryTests do 7 | 8 | @tag capture_log: true 9 | test "service events" do 10 | ref = Noizu.SimplePool.TestHelpers.unique_ref(:two) 11 | TestTwoPool.Server.test_s_call!(ref, :bannana, @context) 12 | TestTwoPool.Server.kill!(ref, @context) 13 | Process.sleep(100) 14 | [start_event, terminate_event] = Noizu.SimplePool.Database.Dispatch.MonitorTable.read!(ref) 15 | assert start_event.event == :start 16 | assert terminate_event.event == :terminate 17 | 18 | service_events = Noizu.SimplePool.Database.MonitoringFramework.Service.EventTable.read!({:"second@127.0.0.1", Noizu.SimplePool.Support.TestTwoPool}) 19 | start_event = List.first(service_events) 20 | assert start_event.entity.identifier == :start 21 | end 22 | 23 | 24 | @tag capture_log: true 25 | test "record events pipes to log output" do 26 | assert capture_log(fn -> 27 | ref = Noizu.SimplePool.TestHelpers.unique_ref() 28 | Noizu.SimplePool.Support.TestPool.Server.fetch(ref, :process, @context) 29 | Process.sleep(500) 30 | end) =~ "[RecordEvent :start]" 31 | end 32 | 33 | 34 | 35 | @tag capture_log: true 36 | test "basic_functionality health check - degraded" do 37 | ref = Noizu.SimplePool.TestHelpers.unique_ref() 38 | Noizu.SimplePool.Support.TestPool.Server.fetch(ref, :process, @context) 39 | 40 | # simulate 3 recent crashes 41 | for _i <- 1..3 do 42 | Process.sleep(1000) 43 | Noizu.SimplePool.Support.TestPool.Server.kill!(ref, @context) 44 | {_r,_p, _s} = wait_for_restart(ref) 45 | # Force sleep so that terminate/start entries are unique (have different time entry) 46 | end 47 | 48 | Process.sleep(1000) 49 | r = Noizu.SimplePool.Support.TestPool.Server.health_check!(ref, @context) 50 | assert r.status == :degraded 51 | assert r.event_frequency.start == 4 52 | assert r.event_frequency.terminate == 3 53 | #assert r.event_frequency.exit >= 3 54 | end 55 | 56 | @tag capture_log: true 57 | test "basic_functionality health check - critical" do 58 | ref = Noizu.SimplePool.TestHelpers.unique_ref() 59 | Noizu.SimplePool.Support.TestPool.Server.fetch(ref, :process, @context) 60 | 61 | # simulate 3 recent crashes 62 | for _i <- 1..5 do 63 | Process.sleep(1000) 64 | Noizu.SimplePool.Support.TestPool.Server.kill!(ref, @context) 65 | {_r,_p, _s} = wait_for_restart(ref) 66 | # Force sleep so that terminate/start entries are unique (have different time entry) 67 | end 68 | 69 | Process.sleep(1000) 70 | r = Noizu.SimplePool.Support.TestPool.Server.health_check!(ref, @context) 71 | assert r.status == :critical 72 | end 73 | 74 | end 75 | -------------------------------------------------------------------------------- /test/simple_pool/v2/routing_test.exs: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2020 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.V2.RoutingTest do 7 | use ExUnit.Case 8 | 9 | import ExUnit.CaptureLog 10 | require Logger 11 | 12 | @context Noizu.ElixirCore.CallingContext.system(%{}) 13 | 14 | @tag :routing 15 | @tag :v2 16 | #@tag capture_log: true 17 | test "process across node" do 18 | ref = Noizu.SimplePool.TestHelpers.unique_ref_v2(:two) 19 | Noizu.SimplePool.Support.TestV2TwoPool.test_s_call!(ref, :bannana, @context) 20 | {:ack, pid} = Noizu.SimplePool.Support.TestV2TwoPool.Server.worker_management().process!(ref, @context, %{}) 21 | assert is_pid(pid) 22 | end 23 | 24 | @tag :v2 25 | @tag capture_log: true 26 | test "process origin node" do 27 | ref = Noizu.SimplePool.TestHelpers.unique_ref_v2(:one) 28 | Noizu.SimplePool.Support.TestV2Pool.test_s_call!(ref, :bannana, @context) 29 | {:ack, pid} = Noizu.SimplePool.Support.TestV2Pool.Server.worker_management().process!(ref, @context, %{}) 30 | assert is_pid(pid) 31 | end 32 | 33 | end -------------------------------------------------------------------------------- /test/support/erp.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defimpl Noizu.ERP, for: Atom do 7 | def sref(nil), do: nil 8 | def ref(nil), do: nil 9 | def id(nil), do: nil 10 | def entity(nil, _options \\ nil), do: nil 11 | def entity!(nil, _options \\ nil), do: nil 12 | def record(nil, _options \\ nil), do: nil 13 | def record!(nil, _options \\ nil), do: nil 14 | 15 | def id_ok(o) do 16 | r = ref(o) 17 | r && {:ok, r} || {:error, o} 18 | end 19 | def ref_ok(o) do 20 | r = ref(o) 21 | r && {:ok, r} || {:error, o} 22 | end 23 | def sref_ok(o) do 24 | r = sref(o) 25 | r && {:ok, r} || {:error, o} 26 | end 27 | def entity_ok(o, options \\ %{}) do 28 | r = entity(o, options) 29 | r && {:ok, r} || {:error, o} 30 | end 31 | def entity_ok!(o, options \\ %{}) do 32 | r = entity!(o, options) 33 | r && {:ok, r} || {:error, o} 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /test/support/nmid_generator.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2020 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.Test.NmidGenerator do 7 | def generate(_seq, _opts) do 8 | :os.system_time(:micro_seconds) 9 | end 10 | def generate!(seq, opts) do 11 | generate(seq, opts) 12 | end 13 | end 14 | 15 | -------------------------------------------------------------------------------- /test/support/test_database.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2019 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | use Amnesia 7 | 8 | defdatabase Noizu.SimplePool.TestDatabase do 9 | 10 | #-------------------------------------- 11 | # Dispatch 12 | #-------------------------------------- 13 | deftable TestV2Pool.DispatchTable, [:identifier, :server, :entity], type: :set, index: [] do 14 | @type t :: %TestV2Pool.DispatchTable{identifier: tuple, server: atom, entity: Noizu.SimplePool.V2.DispatchEntity.t} 15 | end 16 | 17 | deftable TestV2TwoPool.DispatchTable, [:identifier, :server, :entity], type: :set, index: [] do 18 | @type t :: %TestV2TwoPool.DispatchTable{identifier: tuple, server: atom, entity: Noizu.SimplePool.V2.DispatchEntity.t} 19 | end 20 | 21 | deftable TestV2ThreePool.DispatchTable, [:identifier, :server, :entity], type: :set, index: [] do 22 | @type t :: %TestV2ThreePool.DispatchTable{identifier: tuple, server: atom, entity: Noizu.SimplePool.V2.DispatchEntity.t} 23 | end 24 | 25 | end -------------------------------------------------------------------------------- /test/support/test_wait.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Mix.Tasks.SecondTestNode do 7 | use Mix.Task 8 | 9 | def run(_) do 10 | :ok 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /test/support/v1/services/test_pool/test_pool.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.Support.TestPool do 7 | #alias Noizu.Scaffolding.CallingContext 8 | use Noizu.SimplePool.Behaviour, 9 | default_modules: [:pool_supervisor, :worker_supervisor], 10 | worker_state_entity: Noizu.SimplePool.Support.TestWorkerEntity, 11 | verbose: false 12 | 13 | defmodule Worker do 14 | @vsn 1.0 15 | use Noizu.SimplePool.WorkerBehaviour, 16 | worker_state_entity: Noizu.SimplePool.Support.TestWorkerEntity, 17 | verbose: false 18 | require Logger 19 | end # end worker 20 | 21 | #============================================================================= 22 | # @Server 23 | #============================================================================= 24 | defmodule Server do 25 | @vsn 1.0 26 | use Noizu.SimplePool.ServerBehaviour, 27 | worker_state_entity: Noizu.SimplePool.Support.TestWorkerEntity, 28 | server_monitor: Noizu.MonitoringFramework.EnvironmentPool.Server, 29 | worker_lookup_handler: Noizu.SimplePool.WorkerLookupBehaviour.Dynamic 30 | #alias Noizu.SimplePool.Support.TestWorkerEntity 31 | 32 | #--------------------------------------------------------------------------- 33 | # Convenience Methods 34 | #--------------------------------------------------------------------------- 35 | def test_s_call!(identifier, value, context) do 36 | s_call!(identifier, {:test_s_call!, value}, context) 37 | end 38 | 39 | def test_s_call(identifier, value, context) do 40 | s_call(identifier, {:test_s_call, value}, context) 41 | end 42 | 43 | def test_s_cast!(identifier, value, context) do 44 | s_cast!(identifier, {:test_s_cast!, value}, context) 45 | end 46 | 47 | def test_s_cast(identifier, value, context) do 48 | s_cast(identifier, {:test_s_cast, value}, context) 49 | end 50 | 51 | 52 | 53 | 54 | end # end defmodule GoldenRatio.Components.Gateway.Server 55 | 56 | 57 | end # end defmodule GoldenRatio.Components.Gateway 58 | -------------------------------------------------------------------------------- /test/support/v1/services/test_pool/test_worker_entity.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.Support.TestWorkerEntity do 7 | @vsn 1.0 8 | 9 | #----------------------------------------------------------------------------- 10 | # aliases, imports, uses, 11 | #----------------------------------------------------------------------------- 12 | require Logger 13 | 14 | #----------------------------------------------------------------------------- 15 | # Struct & Types 16 | #----------------------------------------------------------------------------- 17 | @type t :: %__MODULE__{ 18 | identifier: Types.entity_reference, 19 | data: Map.t, 20 | vsn: Types.vsn 21 | } 22 | 23 | defstruct [ 24 | identifier: nil, 25 | data: %{}, 26 | vsn: @vsn 27 | ] 28 | 29 | use Noizu.SimplePool.InnerStateBehaviour, 30 | pool: Noizu.SimplePool.Support.TestPool, 31 | override: [:load, :supervisor_hint] 32 | 33 | def supervisor_hint(ref) do 34 | "test_" <> ts = id(ref) 35 | String.to_integer(ts) 36 | end 37 | 38 | #----------------------------------------------------------------------------- 39 | # Behaviour 40 | #----------------------------------------------------------------------------- 41 | def load(ref), do: load(ref, nil, nil) 42 | def load(ref, context), do: load(ref, nil, context) 43 | def load(ref, _options, _context) do 44 | %__MODULE__{ 45 | identifier: id(ref) 46 | } 47 | end 48 | 49 | #----------------------------------------------------------------------------- 50 | # Implementation 51 | #----------------------------------------------------------------------------- 52 | def test_s_call!(this, value, _context) do 53 | state = put_in(this, [Access.key(:data), :s_call!], value) 54 | {:reply, :s_call!, state} 55 | end 56 | def test_s_call(this, value, _context), do: {:reply, :s_call, put_in(this, [Access.key(:data), :s_call], value)} 57 | def test_s_cast!(this, value, _context), do: {:noreply, put_in(this, [Access.key(:data), :s_cast!], value)} 58 | def test_s_cast(this, value, _context), do: {:noreply, put_in(this, [Access.key(:data), :s_cast], value)} 59 | 60 | #----------------------------------------------------------------------------- 61 | # call_forwarding 62 | #----------------------------------------------------------------------------- 63 | def call_forwarding({:test_s_call!, value}, context, _from, %__MODULE__{} = this), do: test_s_call!(this, value, context) 64 | def call_forwarding({:test_s_call, value}, context, _from, %__MODULE__{} = this), do: test_s_call(this, value, context) 65 | 66 | 67 | def call_forwarding({:test_s_cast!, value}, context, %__MODULE__{} = this), do: test_s_cast!(this, value, context) 68 | def call_forwarding({:test_s_cast, value}, context, %__MODULE__{} = this), do: test_s_cast(this, value, context) 69 | 70 | #------------------- 71 | # id/1 72 | #------------------- 73 | def id({:ref, __MODULE__, identifier}), do: identifier 74 | def id("ref.noizu-test." <> identifier), do: identifier 75 | def id(%__MODULE__{} = entity), do: entity.identifier 76 | 77 | #------------------- 78 | # ref/1 79 | #------------------- 80 | def ref({:ref, __MODULE__, identifier}), do: {:ref, __MODULE__, identifier} 81 | def ref("ref.noizu-test." <> identifier), do: {:ref, __MODULE__, identifier} 82 | def ref(%__MODULE__{} = entity), do: {:ref, __MODULE__, entity.identifier} 83 | 84 | #------------------- 85 | # sref/1 86 | #------------------- 87 | def sref({:ref, __MODULE__, identifier}), do: "ref.noizu-test.#{identifier}" 88 | def sref("ref.noizu-test." <> identifier), do: "ref.noizu-test.#{identifier}" 89 | def sref(%__MODULE__{} = entity), do: "ref.noizu-test.#{entity.identifier}" 90 | 91 | #------------------- 92 | # entity/2 93 | #------------------- 94 | def entity(ref, options \\ %{}) 95 | def entity({:ref, __MODULE__, identifier}, _options), do: %__MODULE__{identifier: identifier} 96 | def entity("ref.noizu-test." <> identifier, _options), do: %__MODULE__{identifier: identifier} 97 | def entity(%__MODULE__{} = entity, _options), do: entity 98 | 99 | #------------------- 100 | # entity!/2 101 | #------------------- 102 | def entity!(ref, options \\ %{}) 103 | def entity!({:ref, __MODULE__, identifier}, _options), do: %__MODULE__{identifier: identifier} 104 | def entity!("ref.noizu-test." <> identifier, _options), do: %__MODULE__{identifier: identifier} 105 | def entity!(%__MODULE__{} = entity, _options), do: entity 106 | 107 | 108 | #------------------- 109 | # record/2 110 | #------------------- 111 | def record(ref, options \\ %{}) 112 | def record({:ref, __MODULE__, identifier}, _options), do: %__MODULE__{identifier: identifier} 113 | def record("ref.noizu-test." <> identifier, _options), do: %__MODULE__{identifier: identifier} 114 | def record(%__MODULE__{} = entity, _options), do: entity 115 | 116 | #------------------- 117 | # record!/2 118 | #------------------- 119 | def record!(ref, options \\ %{}) 120 | def record!({:ref, __MODULE__, identifier}, _options), do: %__MODULE__{identifier: identifier} 121 | def record!("ref.noizu-test." <> identifier, _options), do: %__MODULE__{identifier: identifier} 122 | def record!(%__MODULE__{} = entity, _options), do: entity 123 | 124 | 125 | 126 | def id_ok(o) do 127 | r = ref(o) 128 | r && {:ok, r} || {:error, o} 129 | end 130 | def ref_ok(o) do 131 | r = ref(o) 132 | r && {:ok, r} || {:error, o} 133 | end 134 | def sref_ok(o) do 135 | r = sref(o) 136 | r && {:ok, r} || {:error, o} 137 | end 138 | def entity_ok(o, options \\ %{}) do 139 | r = entity(o, options) 140 | r && {:ok, r} || {:error, o} 141 | end 142 | def entity_ok!(o, options \\ %{}) do 143 | r = entity!(o, options) 144 | r && {:ok, r} || {:error, o} 145 | end 146 | 147 | 148 | defimpl Noizu.ERP, for: Noizu.SimplePool.Support.TestWorkerEntity do 149 | def id(obj) do 150 | obj.identifier 151 | end # end sref/1 152 | 153 | def ref(obj) do 154 | {:ref, Noizu.SimplePool.Support.TestWorkerEntity, obj.identifier} 155 | end # end ref/1 156 | 157 | def sref(obj) do 158 | "ref.noizu-test.#{obj.identifier}" 159 | end # end sref/1 160 | 161 | def record(obj, _options \\ nil) do 162 | obj 163 | end # end record/2 164 | 165 | def record!(obj, _options \\ nil) do 166 | obj 167 | end # end record/2 168 | 169 | def entity(obj, _options \\ nil) do 170 | obj 171 | end # end entity/2 172 | 173 | def entity!(obj, _options \\ nil) do 174 | obj 175 | end # end defimpl EntityReferenceProtocol, for: Tuple 176 | 177 | 178 | 179 | def id_ok(o) do 180 | r = ref(o) 181 | r && {:ok, r} || {:error, o} 182 | end 183 | def ref_ok(o) do 184 | r = ref(o) 185 | r && {:ok, r} || {:error, o} 186 | end 187 | def sref_ok(o) do 188 | r = sref(o) 189 | r && {:ok, r} || {:error, o} 190 | end 191 | def entity_ok(o, options \\ %{}) do 192 | r = entity(o, options) 193 | r && {:ok, r} || {:error, o} 194 | end 195 | def entity_ok!(o, options \\ %{}) do 196 | r = entity!(o, options) 197 | r && {:ok, r} || {:error, o} 198 | end 199 | 200 | end 201 | 202 | 203 | #----------------------------------------------------------------------------- 204 | # Inspect Protocol 205 | #----------------------------------------------------------------------------- 206 | defimpl Inspect, for: Noizu.SimplePool.Support.TestWorkerEntity do 207 | import Inspect.Algebra 208 | def inspect(entity, opts) do 209 | heading = "#TestWorkerEntity(#{inspect entity.identifier})" 210 | {seperator, end_seperator} = if opts.pretty, do: {"\n ", "\n"}, else: {" ", " "} 211 | inner = cond do 212 | opts.limit == :infinity -> 213 | concat(["<#{seperator}", to_doc(Map.from_struct(entity), opts), "#{end_seperator}>"]) 214 | true -> "<>" 215 | end 216 | concat [heading, inner] 217 | end # end inspect/2 218 | end # end defimpl 219 | 220 | end # end defmacro 221 | -------------------------------------------------------------------------------- /test/support/v1/services/test_three_pool/test_three_pool.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.Support.TestThreePool do 7 | #alias Noizu.Scaffolding.CallingContext 8 | use Noizu.SimplePool.Behaviour, 9 | default_modules: [:pool_supervisor, :worker_supervisor], 10 | worker_state_entity: Noizu.SimplePool.Support.TestThreeWorkerEntity, 11 | verbose: false 12 | 13 | defmodule Worker do 14 | @vsn 1.0 15 | use Noizu.SimplePool.WorkerBehaviour, 16 | worker_state_entity: Noizu.SimplePool.Support.TestThreeWorkerEntity, 17 | verbose: false 18 | require Logger 19 | end # end worker 20 | 21 | #============================================================================= 22 | # @Server 23 | #============================================================================= 24 | defmodule Server do 25 | @vsn 1.0 26 | use Noizu.SimplePool.ServerBehaviour, 27 | worker_state_entity: Noizu.SimplePool.Support.TestThreeWorkerEntity, 28 | server_monitor: Noizu.MonitoringFramework.EnvironmentPool.Server, 29 | worker_lookup_handler: Noizu.SimplePool.WorkerLookupBehaviour.Dynamic 30 | 31 | #--------------------------------------------------------------------------- 32 | # Convenience Methods 33 | #--------------------------------------------------------------------------- 34 | def test_s_call!(identifier, value, context) do 35 | s_call!(identifier, {:test_s_call!, value}, context) 36 | end 37 | 38 | def test_s_call(identifier, value, context) do 39 | s_call(identifier, {:test_s_call, value}, context) 40 | end 41 | 42 | def test_s_cast!(identifier, value, context) do 43 | s_cast!(identifier, {:test_s_cast!, value}, context) 44 | end 45 | 46 | def test_s_cast(identifier, value, context) do 47 | s_cast(identifier, {:test_s_cast, value}, context) 48 | end 49 | 50 | 51 | 52 | 53 | end # end defmodule GoldenRatio.Components.Gateway.Server 54 | 55 | 56 | end # end defmodule GoldenRatio.Components.Gateway 57 | -------------------------------------------------------------------------------- /test/support/v1/services/test_two_pool/test_two_pool.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.Support.TestTwoPool do 7 | #alias Noizu.Scaffolding.CallingContext 8 | use Noizu.SimplePool.Behaviour, 9 | default_modules: [:pool_supervisor, :worker_supervisor], 10 | worker_state_entity: Noizu.SimplePool.Support.TestTwoWorkerEntity, 11 | verbose: false 12 | 13 | defmodule Worker do 14 | @vsn 1.0 15 | use Noizu.SimplePool.WorkerBehaviour, 16 | worker_state_entity: Noizu.SimplePool.Support.TestTwoWorkerEntity, 17 | verbose: false 18 | require Logger 19 | end # end worker 20 | 21 | #============================================================================= 22 | # @Server 23 | #============================================================================= 24 | defmodule Server do 25 | @vsn 1.0 26 | use Noizu.SimplePool.ServerBehaviour, 27 | worker_state_entity: Noizu.SimplePool.Support.TestTwoWorkerEntity, 28 | server_monitor: Noizu.MonitoringFramework.EnvironmentPool.Server, 29 | worker_lookup_handler: Noizu.SimplePool.WorkerLookupBehaviour.Dynamic 30 | #alias Noizu.SimplePool.Support.TestTwoWorkerEntity 31 | 32 | #--------------------------------------------------------------------------- 33 | # Convenience Methods 34 | #--------------------------------------------------------------------------- 35 | def test_s_call!(identifier, value, context) do 36 | s_call!(identifier, {:test_s_call!, value}, context) 37 | end 38 | 39 | def test_s_call(identifier, value, context) do 40 | s_call(identifier, {:test_s_call, value}, context) 41 | end 42 | 43 | def test_s_cast!(identifier, value, context) do 44 | s_cast!(identifier, {:test_s_cast!, value}, context) 45 | end 46 | 47 | def test_s_cast(identifier, value, context) do 48 | s_cast(identifier, {:test_s_cast, value}, context) 49 | end 50 | 51 | 52 | 53 | 54 | end # end defmodule GoldenRatio.Components.Gateway.Server 55 | 56 | 57 | end # end defmodule GoldenRatio.Components.Gateway 58 | -------------------------------------------------------------------------------- /test/support/v2/services/test_pool/test_pool.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.Support.TestV2Pool do 7 | #alias Noizu.Scaffolding.CallingContext 8 | use Noizu.SimplePool.V2.PoolBehaviour, 9 | default_modules: [:pool_supervisor, :worker_supervisor, :monitor], 10 | worker_state_entity: Noizu.SimplePool.Support.TestV2WorkerEntity, 11 | dispatch_table: Noizu.SimplePool.TestDatabase.TestV2Pool.DispatchTable, 12 | verbose: false 13 | 14 | defmodule Worker do 15 | @vsn 1.0 16 | use Noizu.SimplePool.V2.WorkerBehaviour, 17 | worker_state_entity: Noizu.SimplePool.Support.TestV2WorkerEntity, 18 | verbose: false 19 | require Logger 20 | end # end worker 21 | 22 | #============================================================================= 23 | # @Server 24 | #============================================================================= 25 | defmodule Server do 26 | @vsn 1.0 27 | use Noizu.SimplePool.V2.ServerBehaviour, 28 | worker_state_entity: Noizu.SimplePool.Support.TestV2WorkerEntity, 29 | worker_lookup_handler: Noizu.SimplePool.WorkerLookupBehaviour.Dynamic 30 | end # end defmodule GoldenRatio.Components.Gateway.Server 31 | 32 | 33 | #--------------------------------------------------------------------------- 34 | # Convenience Methods 35 | #--------------------------------------------------------------------------- 36 | def test_s_call!(identifier, value, context) do 37 | __MODULE__.Server.Router.s_call!(identifier, {:test_s_call!, value}, context) 38 | end 39 | 40 | def test_s_call(identifier, value, context) do 41 | __MODULE__.Server.Router.s_call(identifier, {:test_s_call, value}, context) 42 | end 43 | 44 | def test_s_cast!(identifier, value, context) do 45 | __MODULE__.Server.Router.s_cast!(identifier, {:test_s_cast!, value}, context) 46 | end 47 | 48 | def test_s_cast(identifier, value, context) do 49 | __MODULE__.Server.Router.s_cast(identifier, {:test_s_cast, value}, context) 50 | end 51 | 52 | end # end defmodule GoldenRatio.Components.Gateway 53 | 54 | -------------------------------------------------------------------------------- /test/support/v2/services/test_three_pool/test_three_pool.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.Support.TestV2ThreePool do 7 | #alias Noizu.Scaffolding.CallingContext 8 | use Noizu.SimplePool.V2.PoolBehaviour, 9 | default_modules: [:pool_supervisor, :worker_supervisor, :monitor], 10 | worker_state_entity: Noizu.SimplePool.Support.TestV2ThreeWorkerEntity, 11 | dispatch_table: Noizu.SimplePool.TestDatabase.TestV2ThreePool.DispatchTable, 12 | verbose: false 13 | 14 | defmodule Worker do 15 | @vsn 1.0 16 | use Noizu.SimplePool.V2.WorkerBehaviour, 17 | worker_state_entity: Noizu.SimplePool.Support.TestV2ThreeWorkerEntity, 18 | verbose: false 19 | require Logger 20 | end # end worker 21 | 22 | #============================================================================= 23 | # @Server 24 | #============================================================================= 25 | defmodule Server do 26 | @vsn 1.0 27 | use Noizu.SimplePool.V2.ServerBehaviour, 28 | worker_state_entity: Noizu.SimplePool.Support.TestV2ThreeWorkerEntity, 29 | worker_lookup_handler: Noizu.SimplePool.WorkerLookupBehaviour.Dynamic 30 | end # end defmodule 31 | #--------------------------------------------------------------------------- 32 | # Convenience Methods 33 | #--------------------------------------------------------------------------- 34 | def test_s_call!(identifier, value, context) do 35 | __MODULE__.Server.Router.s_call!(identifier, {:test_s_call!, value}, context) 36 | end 37 | 38 | def test_s_call(identifier, value, context) do 39 | __MODULE__.Server.Router.s_call(identifier, {:test_s_call, value}, context) 40 | end 41 | 42 | def test_s_cast!(identifier, value, context) do 43 | __MODULE__.Server.Router.s_cast!(identifier, {:test_s_cast!, value}, context) 44 | end 45 | 46 | def test_s_cast(identifier, value, context) do 47 | __MODULE__.Server.Router.s_cast(identifier, {:test_s_cast, value}, context) 48 | end 49 | 50 | end # end defmodule 51 | -------------------------------------------------------------------------------- /test/support/v2/services/test_two_pool/test_two_pool.ex: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | defmodule Noizu.SimplePool.Support.TestV2TwoPool do 7 | #alias Noizu.Scaffolding.CallingContext 8 | use Noizu.SimplePool.V2.PoolBehaviour, 9 | default_modules: [:pool_supervisor, :worker_supervisor, :monitor], 10 | worker_state_entity: Noizu.SimplePool.Support.TestV2TwoWorkerEntity, 11 | dispatch_table: Noizu.SimplePool.TestDatabase.TestV2TwoPool.DispatchTable, 12 | verbose: false 13 | 14 | def banner(_header, _msg) do 15 | :succesful_override 16 | end 17 | 18 | defmodule Worker do 19 | @vsn 1.0 20 | use Noizu.SimplePool.V2.WorkerBehaviour, 21 | worker_state_entity: Noizu.SimplePool.Support.TestV2TwoWorkerEntity, 22 | verbose: false 23 | require Logger 24 | end # end worker 25 | 26 | #============================================================================= 27 | # @Server 28 | #============================================================================= 29 | defmodule Server do 30 | @vsn 1.0 31 | use Noizu.SimplePool.V2.ServerBehaviour, 32 | worker_state_entity: Noizu.SimplePool.Support.TestV2TwoWorkerEntity, 33 | worker_lookup_handler: Noizu.SimplePool.WorkerLookupBehaviour.Dynamic 34 | #alias Noizu.SimplePool.Support.TestTwoWorkerEntity 35 | end # end defmodule 36 | 37 | #--------------------------------------------------------------------------- 38 | # Convenience Methods 39 | #--------------------------------------------------------------------------- 40 | def test_s_call!(identifier, value, context) do 41 | __MODULE__.Server.Router.s_call!(identifier, {:test_s_call!, value}, context) 42 | end 43 | 44 | def test_s_call(identifier, value, context) do 45 | __MODULE__.Server.Router.s_call(identifier, {:test_s_call, value}, context) 46 | end 47 | 48 | def test_s_cast!(identifier, value, context) do 49 | __MODULE__.Server.Router.s_cast!(identifier, {:test_s_cast!, value}, context) 50 | end 51 | 52 | def test_s_cast(identifier, value, context) do 53 | __MODULE__.Server.Router.s_cast(identifier, {:test_s_cast, value}, context) 54 | end 55 | 56 | end # end defmodule 57 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Author: Keith Brings 3 | # Copyright (C) 2018 Noizu Labs, Inc. All rights reserved. 4 | #------------------------------------------------------------------------------- 5 | 6 | require Logger 7 | Logger.info """ 8 | 9 | ---------------------------------- 10 | Test Start 11 | ---------------------------------- 12 | """ 13 | ExUnit.start() 14 | 15 | alias Noizu.SimplePool.Support.TestPool 16 | alias Noizu.SimplePool.Support.TestTwoPool 17 | #alias Noizu.SimplePool.Support.TestThreePool 18 | Application.ensure_all_started(:bypass) 19 | Application.ensure_all_started(:semaphore) 20 | 21 | 22 | 23 | #----------------------------------------------- 24 | # Test Schema Setup 25 | #----------------------------------------------- 26 | Amnesia.start 27 | 28 | 29 | 30 | #------------------------- 31 | # V1 Core Tables 32 | #------------------------- 33 | if !Amnesia.Table.exists?(Noizu.SimplePool.Database.DispatchTable) do 34 | :ok = Noizu.SimplePool.Database.DispatchTable.create() 35 | :ok = Noizu.SimplePool.Database.Dispatch.MonitorTable.create() 36 | :ok = Noizu.SimplePool.Database.MonitoringFramework.SettingTable.create() 37 | :ok = Noizu.SimplePool.Database.MonitoringFramework.NodeTable.create() 38 | :ok = Noizu.SimplePool.Database.MonitoringFramework.ServiceTable.create() 39 | :ok = Noizu.SimplePool.Database.MonitoringFramework.Service.HintTable.create() 40 | 41 | :ok = Noizu.SimplePool.Database.MonitoringFramework.Node.EventTable.create() 42 | :ok = Noizu.SimplePool.Database.MonitoringFramework.Service.EventTable.create() 43 | end 44 | 45 | 46 | #------------------------- 47 | # V2 Core Tables 48 | #------------------------- 49 | if !Amnesia.Table.exists?(Noizu.SimplePool.V2.Database.MonitoringFramework.SettingTable) do 50 | :ok = Noizu.SimplePool.V2.Database.MonitoringFramework.SettingTable.create() 51 | :ok = Noizu.SimplePool.V2.Database.MonitoringFramework.ConfigurationTable.create() 52 | :ok = Noizu.SimplePool.V2.Database.MonitoringFramework.NodeTable.create() 53 | :ok = Noizu.SimplePool.V2.Database.MonitoringFramework.ServiceTable.create() 54 | 55 | :ok = Noizu.SimplePool.V2.Database.MonitoringFramework.DetailedServiceEventTable.create() 56 | :ok = Noizu.SimplePool.V2.Database.MonitoringFramework.ServiceEventTable.create() 57 | :ok = Noizu.SimplePool.V2.Database.MonitoringFramework.DetailedServerEventTable.create() 58 | :ok = Noizu.SimplePool.V2.Database.MonitoringFramework.ServerEventTable.create() 59 | :ok = Noizu.SimplePool.V2.Database.MonitoringFramework.ClusterEventTable.create() 60 | end 61 | 62 | #------------------------- 63 | # V2.B Core Tables 64 | #------------------------- 65 | if !Amnesia.Table.exists?(Noizu.SimplePool.V2.Database.Cluster.Service.Instance.StateTable) do 66 | IO.puts "SETUP V2.B Tables" 67 | :ok = Noizu.SimplePool.V2.Database.Cluster.SettingTable.create(memory: [:"first@127.0.0.1"]) 68 | :ok = Noizu.SimplePool.V2.Database.Cluster.StateTable.create(memory: [:"first@127.0.0.1"]) 69 | :ok = Noizu.SimplePool.V2.Database.Cluster.TaskTable.create(memory: [:"first@127.0.0.1"]) 70 | :ok = Noizu.SimplePool.V2.Database.Cluster.Service.StateTable.create(memory: [:"first@127.0.0.1"]) 71 | :ok = Noizu.SimplePool.V2.Database.Cluster.Service.WorkerTable.create(memory: [:"first@127.0.0.1"]) 72 | :ok = Noizu.SimplePool.V2.Database.Cluster.Service.TaskTable.create(memory: [:"first@127.0.0.1"]) 73 | :ok = Noizu.SimplePool.V2.Database.Cluster.Service.Instance.StateTable.create(memory: [:"first@127.0.0.1"]) 74 | :ok = Noizu.SimplePool.V2.Database.Cluster.Node.StateTable.create(memory: [:"first@127.0.0.1"]) 75 | :ok = Noizu.SimplePool.V2.Database.Cluster.Node.WorkerTable.create(memory: [:"first@127.0.0.1"]) 76 | :ok = Noizu.SimplePool.V2.Database.Cluster.Node.TaskTable.create(memory: [:"first@127.0.0.1"]) 77 | end 78 | 79 | 80 | 81 | 82 | #--------------------- 83 | # Test Pool: Dispatch Tables 84 | #--------------------- 85 | if !Amnesia.Table.exists?(Noizu.SimplePool.TestDatabase.TestV2Pool.DispatchTable) do 86 | :ok = Noizu.SimplePool.TestDatabase.TestV2Pool.DispatchTable.create() 87 | end 88 | if !Amnesia.Table.exists?(Noizu.SimplePool.TestDatabase.TestV2TwoPool.DispatchTable) do 89 | :ok = Noizu.SimplePool.TestDatabase.TestV2TwoPool.DispatchTable.create() 90 | end 91 | if !Amnesia.Table.exists?(Noizu.SimplePool.TestDatabase.TestV2ThreePool.DispatchTable) do 92 | :ok = Noizu.SimplePool.TestDatabase.TestV2ThreePool.DispatchTable.create() 93 | end 94 | 95 | 96 | :ok = Amnesia.Table.wait(Noizu.SimplePool.Database.tables(), 5_000) 97 | :ok = Amnesia.Table.wait(Noizu.SimplePool.TestDatabase.tables(), 5_000) 98 | 99 | # Wait for second node 100 | connected = Node.connect(:"second@127.0.0.1") 101 | if (!connected) do 102 | IO.puts "Waiting five minutes for second test node (./test-node.sh)" 103 | case Noizu.SimplePool.TestHelpers.wait_for_condition(fn() -> (Node.connect(:"second@127.0.0.1") == true) end, 60 * 5) do 104 | :ok -> 105 | IO.puts "Second Node Online" 106 | {:error, :timeout} -> 107 | IO.puts "Timeout Occurred waiting for Second Node" 108 | exit(:shutdown) 109 | end 110 | end 111 | 112 | # Wait for connectivity / compile 113 | Noizu.SimplePool.TestHelpers.wait_for_condition( 114 | fn() -> 115 | :rpc.call(:"second@127.0.0.1", Noizu.SimplePool.TestHelpers, :wait_for_init, []) == :ok 116 | end, 117 | 60 * 5 118 | ) 119 | 120 | spawn_second = if !Enum.member?(Amnesia.info(:db_nodes),:"second@127.0.0.1") do 121 | # conditional include to reduce the need to restart the remote server 122 | IO.puts "SPAWN SECOND == true" 123 | :mnesia.change_config(:extra_db_nodes, [:"second@127.0.0.1"]) 124 | true 125 | else 126 | IO.puts "SPAWN SECOND == false" 127 | false 128 | end 129 | 130 | :ok = :rpc.call(:"second@127.0.0.1", Noizu.SimplePool.TestHelpers, :wait_for_db, []) 131 | 132 | #----------------------------------------------- 133 | # Registry and Environment Manager Setup - Local 134 | #----------------------------------------------- 135 | context = Noizu.ElixirCore.CallingContext.system(%{}) 136 | Noizu.SimplePool.TestHelpers.setup_first() 137 | :ok = Noizu.SimplePool.TestHelpers.unique_ref(:one) 138 | |> Noizu.SimplePool.TestHelpers.wait_hint_release(TestPool.Server, context) 139 | 140 | if spawn_second do 141 | IO.puts "Provision Second Node for Test" 142 | {:pid, _second_pid} = :rpc.call(:"second@127.0.0.1", Noizu.SimplePool.TestHelpers, :setup_second, []) 143 | else 144 | IO.puts "Checking second node state" 145 | case :rpc.call(:"second@127.0.0.1", Noizu.MonitoringFramework.EnvironmentPool.Server, :node_health_check!, [context, %{}]) do 146 | {:badrpc, _} -> 147 | {:pid, _second_pid} = :rpc.call(:"second@127.0.0.1", Noizu.SimplePool.TestHelpers, :setup_second, []) 148 | v -> IO.puts "Checking second node state #{inspect v}" 149 | end 150 | end 151 | 152 | IO.puts "Wait For Hint Release" 153 | :ok = Noizu.SimplePool.TestHelpers.unique_ref(:two) 154 | |> Noizu.SimplePool.TestHelpers.wait_hint_release(TestTwoPool.Server, context) 155 | 156 | IO.puts "Wait For Hint Release . . . [PROCEED]" 157 | 158 | if (node() == :"first@127.0.0.1") do 159 | IO.puts "//////////////////////////////////////////////////////" 160 | IO.puts "waiting for TestV2Two to come online" 161 | IO.puts "//////////////////////////////////////////////////////" 162 | # Wait for connectivity / compile 163 | Noizu.SimplePool.TestHelpers.wait_for_condition( 164 | fn() -> 165 | :rpc.call(:"second@127.0.0.1", Noizu.SimplePool.Support.TestV2TwoPool.Server, :server_online?, []) == true 166 | end, 167 | 60 * 5 168 | ) 169 | 170 | IO.puts "//////////////////////////////////////////////////////" 171 | IO.puts "waiting for remote registry" 172 | IO.puts "//////////////////////////////////////////////////////" 173 | 174 | :ok = Noizu.SimplePool.TestHelpers.wait_for_condition( 175 | fn() -> 176 | :rpc.call(:"second@127.0.0.1", Registry, :lookup, [Noizu.SimplePool.Support.TestV2TwoPool.Registry, {:worker, :aple}]) == [] 177 | end, 178 | 60 * 5 179 | ) 180 | [] = :rpc.call(:"second@127.0.0.1", Registry, :lookup, [Noizu.SimplePool.Support.TestV2TwoPool.Registry, {:worker, :aple}]) 181 | 182 | 183 | 184 | IO.puts "//////////////////////////////////////////////////////" 185 | IO.puts "Proceed" 186 | IO.puts "//////////////////////////////////////////////////////" 187 | 188 | end 189 | --------------------------------------------------------------------------------