{"id":13509241,"url":"https://github.com/klarna/ponos","last_synced_at":"2025-05-16T16:31:02.957Z","repository":{"id":147181298,"uuid":"26750343","full_name":"klarna/ponos","owner":"klarna","description":"ponos is a simple yet powerful load generator written in erlang","archived":false,"fork":false,"pushed_at":"2021-10-29T12:07:16.000Z","size":270,"stargazers_count":158,"open_issues_count":2,"forks_count":8,"subscribers_count":32,"default_branch":"master","last_synced_at":"2024-04-22T13:33:55.679Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/klarna.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS"}},"created_at":"2014-11-17T09:49:33.000Z","updated_at":"2024-04-04T11:40:08.000Z","dependencies_parsed_at":null,"dependency_job_id":"33874b79-9d3a-4c65-a814-f33d62597333","html_url":"https://github.com/klarna/ponos","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klarna%2Fponos","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klarna%2Fponos/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klarna%2Fponos/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klarna%2Fponos/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/klarna","download_url":"https://codeload.github.com/klarna/ponos/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254567288,"owners_count":22092742,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T02:01:05.047Z","updated_at":"2025-05-16T16:31:02.523Z","avatar_url":"https://github.com/klarna.png","language":"Erlang","funding_links":[],"categories":["Testing","General Libraries"],"sub_categories":[],"readme":"                       _____   ____  _   _  ____   _____\n                      |  __ \\ / __ \\| \\ | |/ __ \\ / ____|\n                      | |__) | |  | |  \\| | |  | | (___\n                      |  ___/| |  | | . ` | |  | |\\___ \\\n                      | |    | |__| | |\\  | |__| |____) |\n                      |_|     \\____/|_| \\_|\\____/|_____/\n\n## Description\n\nPonos is an Erlang application that exposes a flexible load generator\nAPI. Ponos [[1](#ref1)] is named after the Greek god of hard labor and\ntoil\n([http://en.wikipedia.org/wiki/Ponos](http://en.wikipedia.org/wiki/Ponos)).\n\nWritten by Jonathan Olsson \u003cjonathan@klarna.com\u003e with contributions from\nCons Åhs \u003ccons@klarna.com\u003e\n\n## Resources\n\nThe main place to find information about ponos is the\n[github](https://github.com/klarna/ponos) page.\n\nDocumentation is available as edoc as well as on the [ponos\nwiki](https://github.com/klarna/ponos/wiki).\n\n## Quick Start Guide\n\n    $\u003e git clone https://github.com/klarna/ponos.git\n    $\u003e cd ponos\n    $\u003e make\n    $\u003e erl -pa ebin -s ponos\n    1\u003e Args = [ {name, unique_name_of_type_atom}\n    1\u003e        , {task, fun() -\u003e ok end}\n    1\u003e        , {load_spec, ponos_load_specs:make_constant(10.0)}\n    1\u003e        ].\n    2\u003e ponos:add_load_generators([Args]).\n    3\u003e ponos:init_load_generators().\n    4\u003e application:stop(ponos).\n\n\n## Introduction\n\nPonos is a simple yet powerful erlang application used to generate load\nat configurable frequencies. It's designed to be lightweight, straight\nforward to use and to require minimal configuration.\n\n## Load Generators\n\nThere are only three required parts of a load generator:\n\n* `Name`\n    * A unique identifier (of type `atom()`) used to reference the load\n      generator.\n* `Task`\n    * A callback function of arity 0; the work to be performed in\n      accordance with `LoadSpec`.\n* `LoadSpec`\n    * The load specification defines the characteristic of the load. It\n      is a function that maps time to intensity: `fun(T) -\u003e I` where `T`\n      is passed time in milliseconds and `I` is the intensity expressed\n      as calls per second. The user may define its own specification,\n      but ponos provides typical load patterns such as constant load,\n      bursts, staircase, and sawtooth. See\n      [`ponos_load_specs`](#ponos_load_specs) for a full list of load\n      specifications.\n\n### Adding and Starting Load Generators\n\n    1\u003e Name = foo.\n    2\u003e Task = fun() -\u003e do_your_thing end.\n    3\u003e LoadSpec = ponos_load_specs:make_sawtooth(10, 20.0).\n    4\u003e Options = [{duration, 60*1000}, {auto_init, false}].\n    5\u003e Args = [{name,Name},{task,Task},{load_spec,LoadSpec},{options,Options}].\n    6\u003e [ok] = ponos:add_load_generators([Args]).\n    7\u003e ponos:init_load_generators([Name]).\n\n`ponos:add_load_generators/1` takes a list of proplists where each\nproplist denotes the arguments needed to add a new load generator to\nponos. The `options` argument is optional, and may contain all or none\nof the available options.\n\nOptions:\n\n* `auto_init` - Defaults to `false`. If set to `true`, the load\n  generator will start generating load immediately. Otherwise\n  `ponos:init_load_generators/0|1` have to be called to start generating\n  load.\n* `duration` - Defaults to `infinity`. By default the load generator\n  will run until removed or paused. If set to a `pos_integer`, the load\n  generator will generate load for that many milliseconds.\n* `max_concurrent` - An `integer()` used to limit how many concurrently\n  ongoing tasks the load generator may spawn. Note that this potentially\n  affects the intensity in a negative way, i.e. you may get a lower\n  output than the `load_spec` describes. By default, the load generator\n  may have an infinite amount of concurrently ongoing tasks.\n* `task_runner` - Defaults to `ponos_default_task_runner`. Must be a\n  `module()` implementing the `ponos_task_runner_callbacks` behaviour.\n* `task_runner_args` - Defaults to `[]`. May be of type any(). The value\n  is passed to the `task_runner`'s `init` function.\n\nFor convenience, all operations on load generators permits referring to\na single load generator or a list of generators.\n\n## \u003ca name=\"ponos_load_specs\"\u003e\u003c/a\u003e ponos_load_specs\n\nThis module provides a set of predefined constructors for typical load\npatterns. A load specification defines the characteristics of load and\nis implemented as a function that maps time to intensity: `fun(T) -\u003e I`\nwhere `T` is passed time in milliseconds and `I` is the intensity\nexpressed as calls per second. The user may implement its own load\nspecifications.\n\n\n### A Note About Sampling Intervals and Timers\n\nA load generator is implemented as a gen_server receiving a tick every\nmillisecond. The `LoadSpec` - which is a function of time - thus gets\nsampled at every tick to determine whether load should be sent or\nnot. At each tick the load generator may decide to generate multiple\ncalls, so it is - at least in theory - possible to produce arbitrary\nlarge amount of cps.\n\nIn practice however the load generator itself will become CPU limited\nabove a point that depends on your CPU and/or OS. It is better to use\ntwo - or more - generators to leverage the power of multi-core CPUs if\nsuch high load is required.\n\nThere are other factors that may prevent a load generator to exactly\nreproduce the specified load pattern.\n\nOne of the factors is how timers are implemented in the Erlang VM: a\ntimer may never fire early, but may get delayed indefinitely. This\nmeans when the load generator decides to sleep for 1 ms, it will\nusually end up sleeping for somewhat longer. This will reduce the\nactual sampling frequency of the `LoadSpec`. Even if the system is not\notherwise overloaded you may not assume sampling frequencies above 500\nHz in practice.\n\nThe other limiting factor is that the load generator will favor higher\nintensities over lower ones when the `LoadSpec` is not constant. This\neffect is best explained via an example.\n\nConsider a `LoadSpec` that for even seconds specifies intensity 0.5\nand for odd seconds 4.0. This means ponos will try to trigger tasks at\n1000, 1250, 1500, 1750, and 3000 milliseconds. The actual intensity\nwill be therefore 4.0 for odd seconds, but for even seconds it will\nonly fall to 0.8, not to 0.5. The low intensity interval is simply too\nshort.\n\n## Task Runners\n\nIn essence, `ponos` gathers load generators under a supervisor and\nmakes sure they get a chance to execute their task at the requested\ninterval. Instead of executing the task itself, ponos hands over to a\ntask runner at certain points, e.g. when it is time to trigger a\ntask. For convenience, `ponos` provides two implementations of the\n`ponos_task_runner_callbacks` interface:\n\n* ponos_default_task_runner - takes no extra arguments and simply\n  applies the provided `Task`.\n* ponos_file_task_runner - applies the provided `Task` as well as logs\n  the various events to file.\n\nThe `ponos_task_runner_callbacks` behaviour defines the following interface:\n\n| ponos_task_runner_callbacks behaviour        |\n| -------------------------------------------- |\n| `call(Name, Task, State) -\u003e ok`              |\n| `concurrency_limit(Name, State::any()) -\u003e ok`|\n| `init(Name, Args) -\u003e {ok, State::any()}`     |\n| `pause(Name, State) -\u003e ok`                   |\n| `start(Name, State) -\u003e {ok, NewState::any()` |\n| `terminate(Name, State) -\u003e ok`               |\n\nThe API allows for modifying the state of the task runner at two\noccasions: `init` and `start`. In all other instances, the task runner\nmay view the state but has no possibility to modify it.\n\nPlease refer to `ponos_default_task_runner.erl` and\n`ponos_file_task_runner.` for further reference.\n\n## Versioning\n\nPonos is versioned according to [semantic versioning](http://semver.org/).\n\n## Notes\n\n\u003ca name=\"ref1\"\u003e\u003c/a\u003e[1] The author is aware of the Russian meaning of the\nword *ponos*. I leave it to the Russian to fight this down with the\nGreek. Needless to say, a suitable name is a suitable name.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fklarna%2Fponos","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fklarna%2Fponos","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fklarna%2Fponos/lists"}