Skip to content

Commit

Permalink
New API (#104)
Browse files Browse the repository at this point in the history
* re-architecture to use proper process startup (not using the application.ex)
* Improved API and performance
* Improved Pipeline and CI support
* Improved documentation overall and document upgrade process
  • Loading branch information
ruslandoga authored Dec 5, 2024
1 parent 8c7a5b2 commit 66ec826
Show file tree
Hide file tree
Showing 28 changed files with 728 additions and 1,269 deletions.
2 changes: 1 addition & 1 deletion .formatter.exs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
inputs: ["{mix,.formatter}.exs", "{lib,test}/**/*.{ex,exs}"]
]
58 changes: 34 additions & 24 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,66 @@ name: CI

on:
pull_request:
branches:
- master
push:
branches:
- master
branches: [master]

jobs:
setup:
test:
runs-on: ${{ matrix.os }}

env:
MIX_ENV: test

strategy:
fail-fast: false
# https://hexdocs.pm/elixir/compatibility-and-deprecations.html#between-elixir-and-erlang-otp
matrix:
os: [ubuntu-22.04, ubuntu-20.04]
elixir_version: [1.13, 1.14, 1.15, 1.16]
otp_version: [24, 25, 26]
os: [ubuntu-22.04, ubuntu-24.04]
elixir: [1.17, 1.16, 1.15, 1.14]
otp: [27, 26, 25]
include:
- elixir: 1.17
otp: 27
exclude:
- otp_version: 26
elixir_version: 1.13

runs-on: ${{ matrix.os }}
- elixir: 1.14
otp: 26
- elixir: 1.14
otp: 27
- elixir: 1.15
otp: 27
- elixir: 1.16
otp: 27

steps:
- uses: actions/checkout@v4

- uses: erlef/setup-beam@v1
with:
otp-version: ${{matrix.otp_version}}
elixir-version: ${{matrix.elixir_version}}
otp-version: ${{matrix.otp}}
elixir-version: ${{matrix.elixir}}

- uses: actions/cache@v4
with:
path: |
deps
_build
key: deps-${{ runner.os }}-${{ matrix.otp_version }}-${{ matrix.elixir_version }}-${{ hashFiles('**/mix.lock') }}
key: test-otp-${{ matrix.otp }}-elixir-${{ matrix.elixir }}-ref-${{ github.head_ref || github.ref }}-mix-${{ hashFiles('**/mix.lock') }}
restore-keys: |
deps-${{ runner.os }}-${{ matrix.otp_version }}-${{ matrix.elixir_version }}
test-otp-${{ matrix.otp }}-elixir-${{ matrix.elixir }}-ref-${{ github.head_ref || github.ref }}-mix-
test-otp-${{ matrix.otp }}-elixir-${{ matrix.elixir }}-ref-refs/heads/master-mix-
- run: mix deps.get

- run: mix format --check-formatted

- run: mix deps.unlock --check-unused

- run: mix deps.compile

- run: mix compile --warnings-as-errors

- run: mix credo --strict --format=oneline

- run: mix test --warnings-as-errors --cover

format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
elixir-version: 1
otp-version: 27
- run: mix format --check-formatted
18 changes: 18 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Publish

on:
push:
tags:
- "*"

jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Check out
uses: actions/checkout@v4

- name: Publish package to hex.pm
uses: hipcall/github_action_publish_hex@v1
env:
HEX_API_KEY: ${{ secrets.HEX_API_KEY }}
4 changes: 2 additions & 2 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
elixir 1.16.3-otp-26
erlang 26.2.5
elixir 1.17.3-otp-27
erlang 27.1.2
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Changelog

## Unreleased
## 7.0.0-rc.0 - 2024-12-04

- Breaking change. Completely new API. Consider upgrading if you are experiencing performance or usability problems with Hammer v6. See [./guides/upgrade-v7.md] for upgrade instructions. https://github.com/ExHammer/hammer/pull/104
- Hammer.Plug has been removed. See documentation for using Hammer as a plug in Phoenix.

## 6.2.1 - 2024-02-23

Expand All @@ -10,7 +13,7 @@

## 6.2.0 - 2024-01-31

- Ensure Elixir version is ~> 1.13 https://github.com/ExHammer/hammer/pull/79.
- Ensure Elixir version is ~> 1.13 https://github.com/ExHammer/hammer/pull/79.

## 6.1.0 - 2022-06-13

Expand Down
1 change: 1 addition & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ Thanks to our Contributors as well:
- [Gary Rennie](https://github.com/Gazler)
- [Ross Wilson](https://github.com/rosswilson)
- [Emmanuel Pinault](https://github.com/epinault)
- [ruslandoga](https://github.com/ruslandoga)
83 changes: 32 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,88 +1,69 @@
<img src="assets/horizontal.svg" alt="hammer" height="150px">

# Hammer

[![Build Status](https://github.com/ExHammer/hammer/actions/workflows/ci.yml/badge.svg)](https://github.com/ExHammer/hammer/actions/workflows/ci.yml) [![Hex.pm](https://img.shields.io/hexpm/v/hammer.svg)](https://hex.pm/packages/hammer) [![Documentation](https://img.shields.io/badge/documentation-gray)](https://hexdocs.pm/hammer)
[![Build Status](https://github.com/ExHammer/hammer/actions/workflows/ci.yml/badge.svg)](https://github.com/ExHammer/hammer/actions/workflows/ci.yml)
[![Hex.pm](https://img.shields.io/hexpm/v/hammer.svg)](https://hex.pm/packages/hammer)
[![Documentation](https://img.shields.io/badge/documentation-gray)](https://hexdocs.pm/hammer)
[![Total Download](https://img.shields.io/hexpm/dt/hammer.svg)](https://hex.pm/packages/hammer)
[![License](https://img.shields.io/hexpm/l/hammer.svg)](https://github.com/ExHammer/hammer/blob/master/LICENSE.md)

A rate-limiter for Elixir, with pluggable storage backends.

## Hammer-Plug

We have a helper-library to make adding rate-limiting to your Phoenix
(or other plug-based) application even easier: [Hammer.Plug](https://github.com/ExHammer/hammer-plug).
**Hammer** is a rate-limiter for Elixir with pluggable storage backends. Hammer enables users to set limits on actions performed within specified time intervals, applying per-user or global limits on API requests, file uploads, and more.

## Installation

Hammer is [available in Hex](https://hex.pm/packages/hammer), the package can be installed
by adding `:hammer` to your list of dependencies in `mix.exs`:
Hammer is [available in Hex](https://hex.pm/packages/hammer). Install by adding `:hammer` to your list of dependencies in `mix.exs`:

```elixir
def deps do
[
{:hammer, "~> 6.1"}
{:hammer, "~> 7.0"}
]
end
```

## Documentation
## Default Algorithm

On HexDocs: [https://hexdocs.pm/hammer/frontpage.html](https://hexdocs.pm/hammer/frontpage.html)
By default, Hammer uses a **fixed window counter** to track actions within set time windows, resetting the count at the start of each new window. For example, with a limit of 10 uploads per minute, a user could upload up to 10 files between 12:00:00 and 12:00:59, and up to 10 more between 12:01:00 and 12:01:59. Notice that the user can upload 20 videos in a second if the uploads are timed at the window edges. If this is an issue, it can be worked around with a "bursty" counter which can be implemented with the current API by making two checks, one for the original interval with the total limit, and one for a shorter interval with a fraction of the limit. That would smooth out the number of requests allowed.

The [Tutorial](https://hexdocs.pm/hammer/tutorial.html) is an especially good place to start.
## Core Concepts

## Usage
- **Limit:** Maximum number of actions allowed in a window.
- **Scale:** Duration of the time window (in milliseconds).
- **Key:** Unique identifier (e.g., user ID) to scope the rate limiting.

Example:
## Example Usage

```elixir
defmodule MyApp.VideoUpload do

def upload(video_data, user_id) do
case Hammer.check_rate("upload_video:#{user_id}", 60_000, 5) do
{:allow, _count} ->
# upload the video, somehow
{:deny, _limit} ->
# deny the request
end
end

defmodule MyApp.RateLimit do
use Hammer, backend: :ets
end
```

The `Hammer` module provides the following functions:
MyApp.RateLimit.start_link()

- `check_rate(id, scale_ms, limit)`
- `check_rate_inc(id, scale_ms, limit, increment)`
- `inspect_bucket(id, scale_ms, limit)`
- `delete_buckets(id)`
user_id = 42
key = "upload_video:#{user_id}"
scale = :timer.minutes(1)
limit = 3

Backends are configured via `Mix.Config`:
case MyApp.RateLimit.hit(key, scale, limit) do
{:allow, _count} ->
# upload the video
:ok

```elixir
config :hammer,
backend: {Hammer.Backend.ETS, [expiry_ms: 60_000 * 60 * 4,
cleanup_interval_ms: 60_000 * 10]}
{:deny, retry_after} ->
# deny the request
{:error, :rate_limit, _message = "try again in #{retry_after}ms"}
end
```

See the [Tutorial](https://hexdocs.pm/hammer/tutorial.html) for more.

See the [Hammer Testbed](https://github.com/ExHammer/hammer-testbed) app for an example of
using Hammer in a Phoenix application.

## Available Backends

- [Hammer.Backend.ETS](https://hexdocs.pm/hammer/Hammer.Backend.ETS.html) (provided with Hammer for testing and dev purposes, not very good for production use)
- [Hammer.Backend.Redis](https://github.com/ExHammer/hammer-backend-redis)
- [Hammer.Backend.Mnesia](https://github.com/ExHammer/hammer-backend-mnesia) (beta)

## Getting Help
- [Hammer.ETS](https://hexdocs.pm/hammer/Hammer.ETS.html) (default, can be [distributed](./guides/distributed-ets.md))
- [Hammer.Redis](https://github.com/ExHammer/hammer-backend-redis)
- [Hammer.Mnesia](https://github.com/ExHammer/hammer-backend-mnesia)

If you're having trouble, either open an issue on this repo
## Acknowledgements

Hammer was inspired by the [ExRated](https://github.com/grempe/ex_rated) library, by [grempe](https://github.com/grempe).
Hammer was originally inspired by the [ExRated](https://github.com/grempe/ex_rated) library, by [grempe](https://github.com/grempe).

## License

Expand Down
12 changes: 0 additions & 12 deletions config/config.exs

This file was deleted.

5 changes: 0 additions & 5 deletions coveralls.json

This file was deleted.

79 changes: 0 additions & 79 deletions guides/CreatingBackends.md

This file was deleted.

30 changes: 0 additions & 30 deletions guides/Frontpage.md

This file was deleted.

Loading

0 comments on commit 66ec826

Please sign in to comment.