summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan Bracco <href@random.sh>2020-05-16 12:17:40 +0200
committerJordan Bracco <href@random.sh>2020-05-16 12:22:30 +0200
commit447b4c9a2e99a37c0d17e01e79d786f2c11590f5 (patch)
tree87202b1b8e9b09a46ad862a475af0664e5dd753a
parentRemove ETS backend, add max_retries, add options to limit/3. (diff)
Format and prepare for release
-rw-r--r--.credo.exs164
-rw-r--r--.gitlab-ci.yml40
-rw-r--r--CHANGELOG.md9
-rw-r--r--LICENSE165
-rw-r--r--README.md2
-rw-r--r--lib/concurrent_limiter.ex69
-rw-r--r--mix.exs23
-rw-r--r--mix.lock3
-rw-r--r--test/concurrent_limiter_test.exs53
-rw-r--r--test/samples/multi_limiter.exs69
-rw-r--r--test/samples/update_counter.exs33
11 files changed, 527 insertions, 103 deletions
diff --git a/.credo.exs b/.credo.exs
new file mode 100644
index 0000000..ec6da94
--- /dev/null
+++ b/.credo.exs
@@ -0,0 +1,164 @@
+# This file contains the configuration for Credo and you are probably reading
+# this after creating it with `mix credo.gen.config`.
+#
+# If you find anything wrong or unclear in this file, please report an
+# issue on GitHub: https://github.com/rrrene/credo/issues
+#
+%{
+ #
+ # You can have as many configs as you like in the `configs:` field.
+ configs: [
+ %{
+ #
+ # Run any exec using `mix credo -C <name>`. If no exec name is given
+ # "default" is used.
+ #
+ name: "default",
+ #
+ # These are the files included in the analysis:
+ files: %{
+ #
+ # You can give explicit globs or simply directories.
+ # In the latter case `**/*.{ex,exs}` will be used.
+ #
+ included: ["lib/", "src/", "test/", "web/", "apps/"],
+ excluded: [~r"/_build/", ~r"/deps/", ~r"/node_modules/", ~r"test/samples/"]
+ },
+ #
+ # Load and configure plugins here:
+ #
+ plugins: [],
+ #
+ # If you create your own checks, you must specify the source files for
+ # them here, so they can be loaded by Credo before running the analysis.
+ #
+ requires: [],
+ #
+ # If you want to enforce a style guide and need a more traditional linting
+ # experience, you can change `strict` to `true` below:
+ #
+ strict: true,
+ #
+ # If you want to use uncolored output by default, you can change `color`
+ # to `false` below:
+ #
+ color: true,
+ #
+ # You can customize the parameters of any check by adding a second element
+ # to the tuple.
+ #
+ # To disable a check put `false` as second element:
+ #
+ # {Credo.Check.Design.DuplicatedCode, false}
+ #
+ checks: [
+ #
+ ## Consistency Checks
+ #
+ {Credo.Check.Consistency.ExceptionNames, []},
+ {Credo.Check.Consistency.LineEndings, []},
+ {Credo.Check.Consistency.ParameterPatternMatching, []},
+ {Credo.Check.Consistency.SpaceAroundOperators, []},
+ {Credo.Check.Consistency.SpaceInParentheses, []},
+ {Credo.Check.Consistency.TabsOrSpaces, []},
+
+ #
+ ## Design Checks
+ #
+ # You can customize the priority of any check
+ # Priority values are: `low, normal, high, higher`
+ #
+ {Credo.Check.Design.AliasUsage,
+ [priority: :low, if_nested_deeper_than: 2, if_called_more_often_than: 0]},
+ # You can also customize the exit_status of each check.
+ # If you don't want TODO comments to cause `mix credo` to fail, just
+ # set this value to 0 (zero).
+ #
+ {Credo.Check.Design.TagTODO, [exit_status: 2]},
+ {Credo.Check.Design.TagFIXME, []},
+
+ #
+ ## Readability Checks
+ #
+ {Credo.Check.Readability.AliasOrder, []},
+ {Credo.Check.Readability.FunctionNames, []},
+ {Credo.Check.Readability.LargeNumbers, []},
+ {Credo.Check.Readability.MaxLineLength, [priority: :low, max_length: 120]},
+ {Credo.Check.Readability.ModuleAttributeNames, []},
+ {Credo.Check.Readability.ModuleDoc, []},
+ {Credo.Check.Readability.ModuleNames, []},
+ {Credo.Check.Readability.ParenthesesInCondition, []},
+ {Credo.Check.Readability.ParenthesesOnZeroArityDefs, []},
+ {Credo.Check.Readability.PredicateFunctionNames, []},
+ {Credo.Check.Readability.PreferImplicitTry, []},
+ {Credo.Check.Readability.RedundantBlankLines, []},
+ {Credo.Check.Readability.Semicolons, []},
+ {Credo.Check.Readability.SpaceAfterCommas, []},
+ {Credo.Check.Readability.StringSigils, []},
+ {Credo.Check.Readability.TrailingBlankLine, []},
+ {Credo.Check.Readability.TrailingWhiteSpace, []},
+ # TODO: enable by default in Credo 1.1
+ {Credo.Check.Readability.UnnecessaryAliasExpansion, false},
+ {Credo.Check.Readability.VariableNames, []},
+
+ #
+ ## Refactoring Opportunities
+ #
+ {Credo.Check.Refactor.CondStatements, []},
+ {Credo.Check.Refactor.CyclomaticComplexity, []},
+ {Credo.Check.Refactor.FunctionArity, []},
+ {Credo.Check.Refactor.LongQuoteBlocks, []},
+ {Credo.Check.Refactor.MapInto, []},
+ {Credo.Check.Refactor.MatchInCondition, []},
+ {Credo.Check.Refactor.NegatedConditionsInUnless, []},
+ {Credo.Check.Refactor.NegatedConditionsWithElse, []},
+ {Credo.Check.Refactor.Nesting, []},
+ {Credo.Check.Refactor.UnlessWithElse, []},
+ {Credo.Check.Refactor.WithClauses, []},
+
+ #
+ ## Warnings
+ #
+ {Credo.Check.Warning.BoolOperationOnSameValues, []},
+ {Credo.Check.Warning.ExpensiveEmptyEnumCheck, []},
+ {Credo.Check.Warning.IExPry, []},
+ {Credo.Check.Warning.IoInspect, []},
+ {Credo.Check.Warning.LazyLogging, []},
+ {Credo.Check.Warning.OperationOnSameValues, []},
+ {Credo.Check.Warning.OperationWithConstantResult, []},
+ {Credo.Check.Warning.RaiseInsideRescue, []},
+ {Credo.Check.Warning.UnusedEnumOperation, []},
+ {Credo.Check.Warning.UnusedFileOperation, []},
+ {Credo.Check.Warning.UnusedKeywordOperation, []},
+ {Credo.Check.Warning.UnusedListOperation, []},
+ {Credo.Check.Warning.UnusedPathOperation, []},
+ {Credo.Check.Warning.UnusedRegexOperation, []},
+ {Credo.Check.Warning.UnusedStringOperation, []},
+ {Credo.Check.Warning.UnusedTupleOperation, []},
+
+ #
+ # Controversial and experimental checks (opt-in, just replace `false` with `[]`)
+ #
+ {Credo.Check.Consistency.MultiAliasImportRequireUse, false},
+ {Credo.Check.Consistency.UnusedVariableNames, false},
+ {Credo.Check.Design.DuplicatedCode, false},
+ {Credo.Check.Readability.AliasAs, false},
+ {Credo.Check.Readability.MultiAlias, false},
+ {Credo.Check.Readability.Specs, false},
+ {Credo.Check.Readability.SinglePipe, false},
+ {Credo.Check.Refactor.ABCSize, false},
+ {Credo.Check.Refactor.AppendSingleItem, false},
+ {Credo.Check.Refactor.DoubleBooleanNegation, false},
+ {Credo.Check.Refactor.ModuleDependencies, false},
+ {Credo.Check.Refactor.PipeChainStart, false},
+ {Credo.Check.Refactor.VariableRebinding, false},
+ {Credo.Check.Warning.MapGetUnsafePass, false},
+ {Credo.Check.Warning.UnsafeToAtom, false}
+
+ #
+ # Custom checks can be created using `mix credo.gen.check`.
+ #
+ ]
+ }
+ ]
+}
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..a580fed
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,40 @@
+image: elixir:1.9
+
+variables:
+ MIX_ENV: test
+
+cache:
+ key: ${CI_COMMIT_REF_SLUG}
+ paths:
+ - deps
+ - _build
+stages:
+ - build
+ - test
+
+before_script:
+ - mix local.hex --force
+ - mix local.rebar --force
+
+build:
+ stage: build
+ script:
+ - mix deps.get
+ - mix compile --force
+
+unit-testing:
+ stage: test
+ coverage: '/(\d+\.\d+\%) \| Total/'
+ script:
+ - mix test --trace --cover
+
+lint:
+ stage: test
+ script:
+ - mix format --check-formatted
+
+analysis:
+ stage: test
+ script:
+ - mix deps.get
+ - mix credo --strict
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..2543ae7
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,9 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
+
+## Unreleased
+
+Initial release
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0a04128
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/README.md b/README.md
index 5886716..7cf6b50 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,3 @@
# Concurrent Limiter
-See the docs in `lib/concurrent_limiter.ex`.
+See the docs at [hexdocs.pm/concurrent_limiter](https://hexdocs.pm/concurrent_limiter/) or in `lib/concurrent_limiter.ex`.
diff --git a/lib/concurrent_limiter.ex b/lib/concurrent_limiter.ex
index b4572c6..9555f00 100644
--- a/lib/concurrent_limiter.ex
+++ b/lib/concurrent_limiter.ex
@@ -1,54 +1,71 @@
+# ConcurrentLimiter: A concurrency limiter.
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: LGPL-3.0-only
+
defmodule ConcurrentLimiter do
require Logger
@moduledoc """
- # Concurrent Limiter
-
A concurrency limiter. Limits the number of concurrent invocations possible, without using a worker pool or different processes.
It can be useful in cases where you don't need a worker pool but still being able to limit concurrent calls without much overhead.
- As it internally uses `persistent_term` to store metadata, and can fallback to ETS tables, it is however not made for a large number
- of limiters and cannot be used for things like a per-user rate limiter.
- """
+ As it internally uses `persistent_term` to store metadata, it is not made for a large number of different or dynamic limiters and
+ cannot be used for things like a per-user rate limiter.
- @doc """
- Initializes a `ConcurrentLimiter`.
+ ```elixir
+ :ok = ConcurrentLimiter.new(RequestLimiter, 10, 10)
+ ConcurrentLimiter.limit(RequestLimiter, fn() -> something_that_can_only_run_ten_times_concurrently() end)
+ ```
"""
@default_wait 150
@default_max_retries 5
- @spec new(name, max_running, max_waiting, options) :: :ok | {:error, :existing} when name: atom(),
- max_running: non_neg_integer(),
- max_waiting: non_neg_integer() | :infinity,
- options: [option],
- option: {:wait, non_neg_integer()} | {:max_retries, non_neg_integer()}
+ @doc "Initializes a `ConcurrentLimiter`."
+ @spec new(name, max_running, max_waiting, options) :: :ok | {:error, :existing}
+ when name: atom(),
+ max_running: non_neg_integer(),
+ max_waiting: non_neg_integer() | :infinity,
+ options: [option],
+ option: {:wait, non_neg_integer()} | {:max_retries, non_neg_integer()}
def new(name, max_running, max_waiting, options \\ []) do
name = prefix_name(name)
+
if defined?(name) do
{:error, :existing}
else
wait = Keyword.get(options, :wait, @default_wait)
max_retries = Keyword.get(options, :max_retries, @default_max_retries)
- atomics = :atomics.new(1, [signed: true])
- :persistent_term.put(name, {__MODULE__, max_running, max_waiting, atomics, wait, max_retries})
+ atomics = :atomics.new(1, signed: true)
+
+ :persistent_term.put(
+ name,
+ {__MODULE__, max_running, max_waiting, atomics, wait, max_retries}
+ )
+
:ok
end
end
- @spec set(name, new_max_running, new_max_waiting, options) :: :ok | :error when name: atom(),
- new_max_running: non_neg_integer(),
- new_max_waiting: non_neg_integer() | :infinity,
- options: [option],
- option: {:wait, non_neg_integer()}
- @doc "Adjust the limiter limits at runtime"
+ @doc "Adjust the limits at runtime."
+ @spec set(name, new_max_running, new_max_waiting, options) :: :ok | :error
+ when name: atom(),
+ new_max_running: non_neg_integer(),
+ new_max_waiting: non_neg_integer() | :infinity,
+ options: [option],
+ option: {:wait, non_neg_integer()}
def set(name, new_max_running, new_max_waiting, options \\ []) do
name = prefix_name(name)
+
if defined?(name) do
new_wait = Keyword.get(options, :wait)
new_max_retries = Keyword.get(options, :max_retries)
{__MODULE__, max_running, max_waiting, ref, wait, max_retries} = :persistent_term.get(name)
- new = {__MODULE__, new_max_running || max_running, new_max_waiting || max_waiting, ref, new_wait || wait, new_max_retries || max_retries}
+
+ new =
+ {__MODULE__, new_max_running || max_running, new_max_waiting || max_waiting, ref,
+ new_wait || wait, new_max_retries || max_retries}
+
:persistent_term.put(name, new)
:ok
else
@@ -56,9 +73,10 @@ defmodule ConcurrentLimiter do
end
end
- @spec limit(atom(), function(), opts) :: {:error, :overload} | any() when opts: [option],
- option: {:wait, non_neg_integer()} | {:max_retries, non_neg_integer()}
@doc "Limits invocation of `fun`."
+ @spec limit(atom(), function(), opts) :: {:error, :overload} | any()
+ when opts: [option],
+ option: {:wait, non_neg_integer()} | {:max_retries, non_neg_integer()}
def limit(name, fun, opts \\ []) do
do_limit(prefix_name(name), fun, opts, 0)
end
@@ -85,11 +103,11 @@ defmodule ConcurrentLimiter do
{:error, :overload}
counter > max_running ->
- wait(ref, name, wait, fun, opts, max_retries, retries + 1)
+ wait(ref, name, fun, wait, opts, retries + 1)
end
end
- defp wait(ref, name, wait, fun, opts, max_retries, retries) do
+ defp wait(ref, name, fun, wait, opts, retries) do
wait = Keyword.get(opts, :timeout) || wait
Process.sleep(wait)
dec(ref, name)
@@ -112,5 +130,4 @@ defmodule ConcurrentLimiter do
rescue
_ -> false
end
-
end
diff --git a/mix.exs b/mix.exs
index 49378b6..93dc17e 100644
--- a/mix.exs
+++ b/mix.exs
@@ -1,13 +1,24 @@
defmodule ConcurrentLimiter.MixProject do
use Mix.Project
+ @repo "https://git.pleroma.social/pleroma/elixir-libraries/concurrent_limiter"
def project do
[
app: :concurrent_limiter,
version: "0.1.0",
- elixir: "~> 1.10",
+ elixir: "~> 1.9",
start_permanent: Mix.env() == :prod,
- deps: deps()
+ deps: deps(),
+ package: package(),
+ # Docs
+ name: "Concurrent Limiter",
+ source_url: @repo,
+ homepage_url: @repo,
+ docs: [
+ main: "ConcurrentLimiter",
+ extras: [],
+ source_url_pattern: @repo <> "/blob/master/%{path}#L%{line}"
+ ]
]
end
@@ -19,8 +30,16 @@ defmodule ConcurrentLimiter.MixProject do
defp deps do
[
+ {:credo, "~> 1.1.0", only: [:dev, :test], runtime: false},
{:ex_doc, "~> 0.21", only: :dev, runtime: false},
{:benchee, "~> 1.0", only: [:dev, :test]}
]
end
+
+ defp package do
+ [
+ licenses: ["LGPLv3"],
+ links: %{"GitLab" => @repo}
+ ]
+ end
end
diff --git a/mix.lock b/mix.lock
index fa32375..98a2640 100644
--- a/mix.lock
+++ b/mix.lock
@@ -1,8 +1,11 @@
%{
"benchee": {:hex, :benchee, "1.0.1", "66b211f9bfd84bd97e6d1beaddf8fc2312aaabe192f776e8931cb0c16f53a521", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}], "hexpm", "3ad58ae787e9c7c94dd7ceda3b587ec2c64604563e049b2a0e8baafae832addb"},
+ "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
+ "credo": {:hex, :credo, "1.1.5", "caec7a3cadd2e58609d7ee25b3931b129e739e070539ad1a0cd7efeeb47014f4", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "d0bbd3222607ccaaac5c0340f7f525c627ae4d7aee6c8c8c108922620c5b6446"},
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
"earmark": {:hex, :earmark, "1.4.4", "4821b8d05cda507189d51f2caeef370cf1e18ca5d7dfb7d31e9cafe6688106a4", [:mix], [], "hexpm", "1f93aba7340574847c0f609da787f0d79efcab51b044bb6e242cae5aca9d264d"},
"ex_doc": {:hex, :ex_doc, "0.21.3", "857ec876b35a587c5d9148a2512e952e24c24345552259464b98bfbb883c7b42", [:mix], [{:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "0db1ee8d1547ab4877c5b5dffc6604ef9454e189928d5ba8967d4a58a801f161"},
+ "jason": {:hex, :jason, "1.2.1", "12b22825e22f468c02eb3e4b9985f3d0cb8dc40b9bd704730efa11abd2708c44", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b659b8571deedf60f79c5a608e15414085fa141344e2716fbd6988a084b5f993"},
"makeup": {:hex, :makeup, "1.0.1", "82f332e461dc6c79dbd82fbe2a9c10d48ed07146f0a478286e590c83c52010b5", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "49736fe5b66a08d8575bf5321d716bac5da20c8e6b97714fec2bcd6febcfa1f8"},
"makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "d4b316c7222a85bbaa2fd7c6e90e37e953257ad196dc229505137c5e505e9eff"},
"nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"},
diff --git a/test/concurrent_limiter_test.exs b/test/concurrent_limiter_test.exs
index e1d281e..090417e 100644
--- a/test/concurrent_limiter_test.exs
+++ b/test/concurrent_limiter_test.exs
@@ -1,42 +1,39 @@
+# ConcurrentLimiter: A concurrency limiter.
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: LGPL-3.0-only
+
defmodule ConcurrentLimiterTest do
use ExUnit.Case
doctest ConcurrentLimiter
- test "limiter ets is atomic" do
- name = "test1"
+ test "limiter is atomic" do
+ name = "test"
ConcurrentLimiter.new(name, 2, 2)
- atomic_test(name)
- end
-
- test "limiter atomics is atomic" do
- name = "test2"
- ConcurrentLimiter.new(name, 2, 2, backend: :atomics)
- atomic_test(name)
- end
-
- defp atomic_test(name) do
self = self()
- sleepy = fn sleep ->
- case ConcurrentLimiter.limit(name, fn ->
- send(self, :ok)
- Process.sleep(sleep)
- :ok
- end) do
- :ok -> :ok
- other -> send(self, other)
- end
- end
-
- spawn_link(fn -> sleepy.(500) end)
- spawn_link(fn -> sleepy.(500) end)
- spawn_link(fn -> sleepy.(500) end)
- spawn_link(fn -> sleepy.(500) end)
- spawn_link(fn -> sleepy.(500) end)
+ spawn_link(fn -> sleepy(name, 500) end)
+ spawn_link(fn -> sleepy(name, 500) end)
+ spawn_link(fn -> sleepy(name, 500) end)
+ spawn_link(fn -> sleepy(name, 500) end)
+ spawn_link(fn -> sleepy(name, 500) end)
assert_receive :ok, 2000
assert_receive :ok, 2000
assert_receive {:error, :overload}, 2000
assert_receive :ok, 2000
assert_receive :ok, 2000
end
+
+ defp sleepy(duration) do
+ result =
+ ConcurrentLimiter.limit(name, fn ->
+ send(self, :ok)
+ Process.sleep(sleep)
+ :ok
+ end)
+
+ case result do
+ :ok -> :ok
+ other -> send(self, other)
+ end
+ end
end
diff --git a/test/samples/multi_limiter.exs b/test/samples/multi_limiter.exs
index f56cb6c..0936773 100644
--- a/test/samples/multi_limiter.exs
+++ b/test/samples/multi_limiter.exs
@@ -1,49 +1,54 @@
infinite = 1_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000
-parallel = case Integer.parse(System.get_env("PARALLEL", "")) do
- {int, _} -> int
- _ -> System.schedulers_online()/2
-end
-multi_count = case Integer.parse(System.get_env("MULTI", "")) do
- {int, _} -> int
- _ -> parallel
-end
+parallel =
+ case Integer.parse(System.get_env("PARALLEL", "")) do
+ {int, _} -> int
+ _ -> System.schedulers_online() / 2
+ end
+
+multi_count =
+ case Integer.parse(System.get_env("MULTI", "")) do
+ {int, _} -> int
+ _ -> parallel
+ end
-names = fn(prefix) ->
+names = fn prefix ->
for i <- 1..multi_count do
Module.concat(MultiConcurrentLimiterBenchmark, "#{prefix}#{i}")
end
end
+bench_unique =
+ for name <- names.("u") do
+ ConcurrentLimiter.new(name, infinite, 0, backend: {:ets, name, []})
+ name
+ end
-bench_unique = for name <- names.("u") do
- ConcurrentLimiter.new(name, infinite, 0, backend: {:ets, name, []})
- name
-end
-
-IO.inspect(bench_unique)
-
-bench_atomics = for name <- names.("a") do
- ConcurrentLimiter.new(name, infinite, 0, backend: :atomics)
- name
-end
+bench_atomics =
+ for name <- names.("a") do
+ ConcurrentLimiter.new(name, infinite, 0, backend: :atomics)
+ name
+ end
-bench_shared = for name <- names.("s") do
- ConcurrentLimiter.new(name, infinite, 0, backend: {:ets, ConcurrentLimiterTest, []})
- name
-end
+bench_shared =
+ for name <- names.("s") do
+ ConcurrentLimiter.new(name, infinite, 0, backend: {:ets, ConcurrentLimiterTest, []})
+ name
+ end
rw = [{:read_concurrency, true}, {:write_concurrency, true}]
-bench_unique_rw = for name <- names.("u_rw") do
- ConcurrentLimiter.new(name, infinite, 0, backend: {:ets, name, rw})
- name
-end
+bench_unique_rw =
+ for name <- names.("u_rw") do
+ ConcurrentLimiter.new(name, infinite, 0, backend: {:ets, name, rw})
+ name
+ end
-bench_shared_rw = for name <- names.("s_rw") do
- ConcurrentLimiter.new(name, infinite, 0, backend: {:ets, ConcurrentLimiterTestRW, rw})
- name
-end
+bench_shared_rw =
+ for name <- names.("s_rw") do
+ ConcurrentLimiter.new(name, infinite, 0, backend: {:ets, ConcurrentLimiterTestRW, rw})
+ name
+ end
multiple = %{
"ConcurrentLimiter.limit/2 unique ets" => fn ->
diff --git a/test/samples/update_counter.exs b/test/samples/update_counter.exs
index 6310c57..1d8e837 100644
--- a/test/samples/update_counter.exs
+++ b/test/samples/update_counter.exs
@@ -1,20 +1,25 @@
:ets.new(:limiter_bench, [:public, :named_table])
-:ets.new(:limiter_bench_concurrent, [:public, :named_table, {:read_concurrency, false}, {:write_concurrency, true}])
+
+:ets.new(:limiter_bench_concurrent, [
+ :public,
+ :named_table,
+ {:read_concurrency, false},
+ {:write_concurrency, true}
+])
+
atomics = :atomics.new(1, [])
-update_counter =
- %{
- "ets:update_counter" => fn ->
- :ets.update_counter(:limiter_bench, "bench", {2, 1}, {"bench", 0})
- end,
- "ets:update_counter concurrent" => fn ->
- :ets.update_counter(:limiter_bench, "bench", {2, 1}, {"bench", 0})
- end,
- "atomics:add_get" => fn ->
- :atomics.add_get(atomics, 1, 1)
- end,
- }
+update_counter = %{
+ "ets:update_counter" => fn ->
+ :ets.update_counter(:limiter_bench, "bench", {2, 1}, {"bench", 0})
+ end,
+ "ets:update_counter concurrent" => fn ->
+ :ets.update_counter(:limiter_bench, "bench", {2, 1}, {"bench", 0})
+ end,
+ "atomics:add_get" => fn ->
+ :atomics.add_get(atomics, 1, 1)
+ end
+}
Benchee.run(update_counter, parallel: 1)
Benchee.run(update_counter, parallel: System.schedulers_online())
-