diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5a561e4..0a2e44f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -12,7 +12,7 @@ jobs:
matrix:
include:
- pair:
- elixir: '1.14'
+ elixir: '1.15'
otp: '24.3'
postgres: '12.13-alpine'
exclude_tags: 'lite'
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..f772ede
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,202 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2025 The Oban Team
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/README.md b/README.md
index afe51f8..ddcc0c1 100644
--- a/README.md
+++ b/README.md
@@ -1,40 +1,159 @@
-# ObanMet
+# Oban Met
-### Rationale
+
+
+
+
-* Charts are helpful for monitoring health and troubleshooting from within Oban Web
-* Not all apps can, or will, run an extra timeseries or metrics database
-* State counts are driven by Postgres, but counting large tables is extremely slow; this requires something novel
+
+
+
-#### Setup
+
+
+
-* Oban.Met is a new package that's a dependency of ObanWeb and ObanPro, but could stand alone
-* Oban.Met listens for [:oban, :supervisor, :start] events and starts a local ets table for each distinct instance
+
+
+
+
-### Recording
+
-* Telemetry events for inserting, fetching, pruning, staging, etc. are recorded as counts
-* State data is recorded as counts with labels for queue, worker, node, as well as a the "old" state for tracking
-* Telemetry events for job execution are recorded as histograms, with the same labels
-* Local collectors broadcast updates periodically, every 1s or so, using pubsub
+Met is a distributed, compacting, multidimensional, telemetry-powered time series datastore for
+Oban that requires no configurion. It gathers data for queues, job counts, execution metrics,
+active crontabs, historic metrics, and more.
-### Collecting
+Met powers the charts and runtime details shown in the [Oban Web][web] dashboard.
-* Oban.Web nodes collect broadcasted metrics and store them in ETS with a local timestamp
-* Collected data is periodically compacted by time, e.g. keep per-second data for 5 minutes, per-minute data for 2 hours, and so on
-* Counts (state, queue, etc) have periodic "keyframes" to ensure consistency from the database
-* Counts between keyframes apply deltas for each state, e.g. executing +1, +1, -1
-* Before shutdown the collector hands off the full database (this could get plopped into a db table instead, not sure)
-* (There are some critical details missing here)
+[web]: https://github.com/oban-bg/oban_web
-### Querying
+## Features
-* Oban.Met provides a few flexible functions to extract counts and summaries from stored data
-* Counts include subgrouping (this is a detail of how I'd like to chart, not critical)
-* Summaries pull from histograms, they are aggregates (min/max/p50/p95/p99) over a period of time with a particular resultion
-* Counts and summaries may be filtered by one or more workers/queues/states/nodes
-* There isn't some query language, it's not freeform, just some keyword options for flexibility
+- **🤖 Autonomous** - Supervises a collection of autonomous modules that dynamically start and stop alongside
+ Oban instances without any code changes.
-### Charting
+- **🎩 Distributed** - Metrics are shared between all connected nodes via pubsub. Leadership is used
+ to restrict expensive operations, such as performing counts, to a single node.
-* Not there yet. Maybe it will be an off-the-shelf package, but it's more likely to be html/svg driven by LV
+- **📼 Recorded** - Telemetry events and scraped data are stored in-memory as time series data.
+ Values are stored as either gauges or space efficient "sketches".
+
+- **🪐 Multidimensional** - Metrics are stored with labels such as `node`, `queue`, `worker`, etc.
+ that can be filtered and grouped dynamically at runtime.
+
+- **🗜️ Compacting** - Time series values are periodically compacted into larger windows of time to
+ save space and optimize querying historic data. Compaction periods use safe defaults, but are
+ configurable.
+
+- **✏️ Estimating** - In supporting systems (Postgres), count queries use optimized estimates
+ automatically for tables with a large number of jobs.
+
+- **🔎 Queryable** - Historic metrics may be filtered and grouped by any label, sliced by
+ arbitrary time intervals, and numeric values aggregated at dynamic percentiles (e.g. P50, P99)
+ without pre-computed histogram buckets.
+
+- **🤝 Handoff** - Ephemeral data storage via data replication with handoff between nodes. All nodes have a
+ shared view of the cluster's data and new nodes are caught up when they come online.
+
+## Installation
+
+Oban Met is included with Oban Web and manual installation is only necessary in hybrid
+environments (separate Web and Worker nodes).
+
+To receive metrics from non-web nodes in a system with separate "web" and "worker" applications
+you must explicitly include `oban_met` as a dependency for "workers".
+
+```elixir
+{:oban_met, "~> 1.0"}
+```
+
+## Usage
+
+No configuration is necessary and Oban Met will start automatically in a typical application. A
+variety of options are provided for more complex or nuanced usage.
+
+### Auto Start
+
+Supervised Met instances start automatically along with Oban instances unless Oban is in testing
+mode. You can disable auto-starting globally with application configuration:
+
+```elixir
+config :oban_met, auto_start: false
+```
+
+Then, start instances as a child directly within your Oban app's plugins:
+
+```elixir
+plugins: [
+ Oban.Met,
+ ...
+]
+```
+
+### Customizing Estimates
+
+Options for internal `Oban.Met` processes can be overridden from the plugin specification. Most
+options are internal and not meant to be overridden, but one particularly useful option to tune is
+the `estimate_limit`. The `estimate_limit` determines at which point state/queue counts switch
+from using an accurate `count(*)` call to a much more efficient, but less accurate, estiamte
+function.
+
+The default limit is a conservative 50k, which may be too low for systems with insert spikes. This
+declares an override to set the limit to 200k:
+
+```elixir
+{Oban.Met, reporter: [estimate_limit: 200_000]}
+```
+
+### Explicit Migrations
+
+Met will create the necessary estimate function automatically when possible. The migration isn't
+necessary under normal circumstances, but is provided to avoid permission issues or allow full
+control over database changes.
+
+```bash
+mix ecto.gen.migration add_oban_met
+```
+
+Open the generated migration and delegate the `up/0` and `down/0` functions to
+`Oban.Met.Migration`:
+
+```elixir
+defmodule MyApp.Repo.Migrations.AddObanMet do
+ use Ecto.Migration
+
+ def up, do: Oban.Met.Migration.up()
+ def down, do: Oban.Met.Migration.down()
+end
+```
+
+Then, after disabling auto-start, configure the reporter not to auto-migrate if you run the
+explicit migration:
+
+```elixir
+{Oban.Met, reporter: [auto_migrate: false]}
+```
+
+
+
+## Contributing
+
+To run the test suite you must have PostgreSQL 12+. Once dependencies are installed, setup the
+databases and run necessary migrations:
+
+```bash
+mix test.setup
+```
+
+## Community
+
+There are a few places to connect and communicate with other Oban users:
+
+- Ask questions and discuss *#oban* on the [Elixir Forum][forum]
+- [Request an invitation][invite] and join the *#oban* channel on Slack
+- Learn about bug reports and upcoming features in the [issue tracker][issues]
+
+[invite]: https://elixir-slack.community/
+[forum]: https://elixirforum.com/
+[issues]: https://github.com/sorentwo/oban/issues
diff --git a/guides/introduction/installation.md b/guides/introduction/installation.md
deleted file mode 100644
index fddeec4..0000000
--- a/guides/introduction/installation.md
+++ /dev/null
@@ -1,75 +0,0 @@
-# Installation
-
-## Usage in Worker Only Nodes
-
-To receive metrics from non-web nodes in a system with separate "web" and "worker" applications you must explicitly include oban_met as a dependency for "workers".
-
-```elixir
-# mix.exs
-defp deps do
- [
- {:oban_met, "~> 0.1", repo: :oban},
- ...
-```
-
-## Auto Start
-
-Supervised Met instances start automatically along with Oban instances unless Oban is in testing
-mode. You can disable auto-starting globally with application configuration:
-
-```elixir
-config :oban_met, auto_start: false
-```
-
-Then, start instances as a child directly within your Oban app's plugins:
-
-```elixir
-plugins: [
- Oban.Met,
- ...
-]
-```
-
-## Customizing Estimates
-
-Options for internal `Oban.Met` processes can be overridden from the plugin specification. Most
-options are internal and not meant to be overridden, but one particularly useful option to tune is
-the `estimate_limit`. The `estimate_limit` determines at which point state/queue counts switch
-from using an accurate `count(*)` call to a much more efficient, but less accurate, estiamte
-function.
-
-The default limit is a conservative 50k, which may be too low for systems with insert spikes. This
-declares an override to set the limit to 200k:
-
-```elixir
-{Oban.Met, reporter: [estimate_limit: 200_000]}
-```
-
-## Explicit Migrations
-
-Met will create the necessary estimate function automatically when possible. The migration isn't
-necessary under normal circumstances, but is provided to avoid permission issues or allow full
-control over database changes.
-
-```bash
-mix ecto.gen.migration add_oban_met
-```
-
-Open the generated migration and delegate the `up/0` and `down/0` functions to
-`Oban.Met.Migration`:
-
-```elixir
-defmodule MyApp.Repo.Migrations.AddObanMet do
- use Ecto.Migration
-
- def up, do: Oban.Met.Migration.up()
- def down, do: Oban.Met.Migration.down()
-end
-```
-
-Then, after disabling auto-start, configure the reporter not to auto-migrate if you run the
-explicit migration:
-
-```elixir
-{Oban.Met, reporter: [auto_migrate: false]}
-```
diff --git a/guides/introduction/overview.md b/guides/introduction/overview.md
deleted file mode 100644
index d84820c..0000000
--- a/guides/introduction/overview.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Overview
-
-🔮 Oban.Met supervises a collection of autonomous modules for in-memory, distributed time-series
-data with zero-configuration. `Oban.Web` relies on `Met` for queue gossip, detailed job counts,
-and historic metrics.
-
-Metrics are gathered and managed automatically for applications using Oban Web. See
-[installation](installation.md) for use in a worker-only node.
-
-## Features
-
-### 📇 Metric Aggregation
-
-Telemetry powered metric tracking and aggregation with compaction.
-
-### 📰 Queue Reporting
-
-Periodic queue checking and reporting (replaces the deprecated `Gossip` plugin).
-
-### 👷♀️ Job Reporting
-
-Periodic counting and reporting with backoff (replaces `Stats` plugin)
-
-### 🎩 Distributed Leadership
-
-Leader backed distributed metric sharing with handoff between nodes.
diff --git a/lib/met.ex b/lib/met.ex
index 5a353f1..5033594 100644
--- a/lib/met.ex
+++ b/lib/met.ex
@@ -1,23 +1,10 @@
defmodule Oban.Met do
- @moduledoc """
- Metric introspection for Oban.
+ @external_resource readme = Path.join([__DIR__, "../README.md"])
- `Oban.Met` supervises a collection of autonomous modules for in-memory, distributed time-series
- data with zero-configuration. `Oban.Web` relies on `Met` for queue gossip, detailed job counts,
- and historic metrics.
-
- ## Highlights
-
- * Telemetry powered execution tracking for time-series data that is replicated between nodes,
- filterable by label, arbitrarily mergeable over windows of time, and compacted for longer
- playback.
-
- * Centralized counting across queues and states with exponential backoff to minimize load and
- data replication between nodes.
-
- * Ephemeral data storage via data replication with handoff between nodes. All nodes have a
- shared view of the cluster's data and new nodes are caught up when they come online.
- """
+ @moduledoc readme
+ |> File.read!()
+ |> String.split("")
+ |> Enum.fetch!(1)
use Supervisor
diff --git a/mix.exs b/mix.exs
index 6df8772..c449152 100644
--- a/mix.exs
+++ b/mix.exs
@@ -1,15 +1,15 @@
defmodule Oban.Met.MixProject do
use Mix.Project
- @version "0.1.11"
+ @source_url "https://github.com/oban-bg/oban_met"
+ @version "1.0.0-dev"
def project do
[
app: :oban_met,
version: @version,
- elixir: "~> 1.13",
+ elixir: "~> 1.15",
elixirc_paths: elixirc_paths(Mix.env()),
- prune_code_paths: false,
start_permanent: Mix.env() == :prod,
deps: deps(),
docs: docs(),
@@ -36,9 +36,6 @@ defmodule Oban.Met.MixProject do
]
end
- defp elixirc_paths(:test), do: ["lib", "test/support"]
- defp elixirc_paths(_env), do: ["lib"]
-
def application do
[
extra_applications: [:logger],
@@ -47,85 +44,41 @@ defmodule Oban.Met.MixProject do
]
end
- def package do
- [
- organization: "oban",
- files: ~w(lib/met.ex lib/oban mix.exs),
- licenses: ["Commercial"],
- links: []
- ]
- end
-
- defp aliases do
- [
- release: [
- "cmd rm -r doc",
- "cmd git tag v#{@version}",
- "cmd git push",
- "cmd git push --tags",
- "hex.publish package --yes",
- "lys.publish"
- ],
- "test.reset": ["ecto.drop --quiet", "test.setup"],
- "test.setup": ["ecto.create --quiet", "ecto.migrate --quiet"],
- "test.ci": [
- "format --check-formatted",
- "deps.unlock --check-unused",
- "credo",
- "test --raise",
- "dialyzer"
- ]
- ]
- end
+ defp elixirc_paths(:test), do: ["lib", "test/support"]
+ defp elixirc_paths(_env), do: ["lib"]
- defp deps do
+ defp package do
[
- {:oban, "~> 2.15"},
- {:telemetry, "~> 1.1"},
- {:ecto_sqlite3, "~> 0.9", only: [:test, :dev]},
- {:postgrex, "~> 0.19", only: [:test, :dev]},
- {:stream_data, "~> 1.1", only: [:test, :dev]},
- {:benchee, "~> 1.3", only: [:test, :dev], runtime: false},
- {:credo, "~> 1.7", only: [:test, :dev], runtime: false},
- {:dialyxir, "~> 1.3", only: [:test, :dev], runtime: false},
- {:ex_doc, "~> 0.34", only: :dev, runtime: false},
- {:makeup_diff, "~> 0.1", only: :dev, runtime: false},
- {:lys_publish, "~> 0.1", only: :dev, path: "../lys_publish", optional: true, runtime: false}
+ maintainers: ["Parker Selbert"],
+ licenses: ["Apache-2.0"],
+ files: ~w(lib .formatter.exs mix.exs README* CHANGELOG* LICENSE*),
+ links: %{
+ Website: "https://oban.pro",
+ Changelog: "#{@source_url}/blob/main/CHANGELOG.md",
+ GitHub: @source_url
+ }
]
end
defp docs do
[
- main: "overview",
+ main: "Oban.Met",
source_ref: "v#{@version}",
formatters: ["html"],
api_reference: false,
extra_section: "GUIDES",
extras: extras(),
- groups_for_extras: groups_for_extras(),
groups_for_modules: groups_for_modules(),
- homepage_url: "/",
- skip_undefined_reference_warnings_on: ["CHANGELOG.md"],
- before_closing_body_tag: fn _ ->
- """
-
- """
- end
+ skip_undefined_reference_warnings_on: ["CHANGELOG.md"]
]
end
defp extras do
[
- "guides/introduction/overview.md",
- "guides/introduction/installation.md",
"CHANGELOG.md": [filename: "changelog", title: "Changelog"]
]
end
- defp groups_for_extras do
- [Introduction: ~r/guides\/introduction\/.?/]
- end
-
defp groups_for_modules do
[
Values: [
@@ -135,4 +88,38 @@ defmodule Oban.Met.MixProject do
]
]
end
+
+ defp deps do
+ [
+ {:oban, "~> 2.18"},
+ {:ecto_sqlite3, "~> 0.18", only: [:test, :dev]},
+ {:postgrex, "~> 0.19", only: [:test, :dev]},
+ {:stream_data, "~> 1.1", only: [:test, :dev]},
+ {:benchee, "~> 1.3", only: [:test, :dev], runtime: false},
+ {:credo, "~> 1.7", only: [:test, :dev], runtime: false},
+ {:dialyxir, "~> 1.3", only: [:test, :dev], runtime: false},
+ {:ex_doc, "~> 0.34", only: :dev, runtime: false},
+ {:makeup_diff, "~> 0.1", only: :dev, runtime: false}
+ ]
+ end
+
+ defp aliases do
+ [
+ release: [
+ "cmd git tag v#{@version} -f",
+ "cmd git push",
+ "cmd git push --tags",
+ "hex.publish --yes"
+ ],
+ "test.reset": ["ecto.drop --quiet", "test.setup"],
+ "test.setup": ["ecto.create --quiet", "ecto.migrate --quiet"],
+ "test.ci": [
+ "format --check-formatted",
+ "deps.unlock --check-unused",
+ "credo",
+ "test --raise",
+ "dialyzer"
+ ]
+ ]
+ end
end
diff --git a/mix.lock b/mix.lock
index 2276533..40a3509 100644
--- a/mix.lock
+++ b/mix.lock
@@ -2,30 +2,29 @@
"benchee": {:hex, :benchee, "1.3.1", "c786e6a76321121a44229dde3988fc772bca73ea75170a73fd5f4ddf1af95ccf", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "76224c58ea1d0391c8309a8ecbfe27d71062878f59bd41a390266bf4ac1cc56d"},
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
"cc_precompiler": {:hex, :cc_precompiler, "0.1.10", "47c9c08d8869cf09b41da36538f62bc1abd3e19e41701c2cea2675b53c704258", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f6e046254e53cd6b41c6bacd70ae728011aa82b2742a80d6e2214855c6e06b22"},
- "credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"},
+ "credo": {:hex, :credo, "1.7.11", "d3e805f7ddf6c9c854fd36f089649d7cf6ba74c42bc3795d587814e3c9847102", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "56826b4306843253a66e47ae45e98e7d284ee1f95d53d1612bb483f88a8cf219"},
"db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"},
- "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
+ "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"},
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
- "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"},
- "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"},
- "ecto": {:hex, :ecto, "3.12.3", "1a9111560731f6c3606924c81c870a68a34c819f6d4f03822f370ea31a582208", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9efd91506ae722f95e48dc49e70d0cb632ede3b7a23896252a60a14ac6d59165"},
- "ecto_sql": {:hex, :ecto_sql, "3.12.0", "73cea17edfa54bde76ee8561b30d29ea08f630959685006d9c6e7d1e59113b7d", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dc9e4d206f274f3947e96142a8fdc5f69a2a6a9abb4649ef5c882323b6d512f0"},
- "ecto_sqlite3": {:hex, :ecto_sqlite3, "0.17.5", "fbee5c17ff6afd8e9ded519b0abb363926c65d30b27577232bb066b2a79957b8", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.12", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:exqlite, "~> 0.22", [hex: :exqlite, repo: "hexpm", optional: false]}], "hexpm", "3b54734d998cbd032ac59403c36acf4e019670e8b6ceef9c6c33d8986c4e9704"},
+ "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"},
+ "earmark_parser": {:hex, :earmark_parser, "1.4.42", "f23d856f41919f17cd06a493923a722d87a2d684f143a1e663c04a2b93100682", [:mix], [], "hexpm", "6915b6ca369b5f7346636a2f41c6a6d78b5af419d61a611079189233358b8b8b"},
+ "ecto": {:hex, :ecto, "3.12.5", "4a312960ce612e17337e7cefcf9be45b95a3be6b36b6f94dfb3d8c361d631866", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6eb18e80bef8bb57e17f5a7f068a1719fbda384d40fc37acb8eb8aeca493b6ea"},
+ "ecto_sql": {:hex, :ecto_sql, "3.12.1", "c0d0d60e85d9ff4631f12bafa454bc392ce8b9ec83531a412c12a0d415a3a4d0", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "aff5b958a899762c5f09028c847569f7dfb9cc9d63bdb8133bff8a5546de6bf5"},
+ "ecto_sqlite3": {:hex, :ecto_sqlite3, "0.18.0", "dd25010d7b536bb00857f3bf47193f293619b0c30538bf912d4bd82861d25fba", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.12", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:exqlite, "~> 0.22", [hex: :exqlite, repo: "hexpm", optional: false]}], "hexpm", "e42ef8252e22c6f65e5421289f9e5ceffd1a4a7e01cb2818d5eab78205d3cce7"},
"elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"},
- "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
- "ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"},
+ "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"},
+ "ex_doc": {:hex, :ex_doc, "0.36.1", "4197d034f93e0b89ec79fac56e226107824adcce8d2dd0a26f5ed3a95efc36b1", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "d7d26a7cf965dacadcd48f9fa7b5953d7d0cfa3b44fa7a65514427da44eafd89"},
"exqlite": {:hex, :exqlite, "0.27.1", "73fc0b3dc3b058a77a2b3771f82a6af2ddcf370b069906968a34083d2ffd2884", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.8", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "79ef5756451cfb022e8013e1ed00d0f8f7d1333c19502c394dc16b15cfb4e9b4"},
- "file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"},
- "hex_core": {:hex, :hex_core, "0.10.0", "6e739a159b0141fa6c3c60c92b73aa6dec5b7909647a9b9ecea9da6709b75709", [:rebar3], [], "hexpm", "1c229aeb2df3a7ffc0c00fa4fc1721995058b2c617f083cf617e29258b1d9f57"},
+ "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
- "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"},
+ "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"},
"makeup_diff": {:hex, :makeup_diff, "0.1.1", "01498f8c95970081297837eaf4686b6f3813e535795b8421f15ace17a59aea37", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "fadb0bf014bd328badb7be986eadbce1a29955dd51c27a9e401c3045cf24184e"},
- "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"},
+ "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"},
"makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
"oban": {:hex, :oban, "2.18.3", "1608c04f8856c108555c379f2f56bc0759149d35fa9d3b825cb8a6769f8ae926", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "36ca6ca84ef6518f9c2c759ea88efd438a3c81d667ba23b02b062a0aa785475e"},
- "postgrex": {:hex, :postgrex, "0.19.1", "73b498508b69aded53907fe48a1fee811be34cc720e69ef4ccd568c8715495ea", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "8bac7885a18f381e091ec6caf41bda7bb8c77912bb0e9285212829afe5d8a8f8"},
+ "postgrex": {:hex, :postgrex, "0.19.3", "a0bda6e3bc75ec07fca5b0a89bffd242ca209a4822a9533e7d3e84ee80707e19", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "d31c28053655b78f47f948c85bb1cf86a9c1f8ead346ba1aa0d0df017fa05b61"},
"statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"},
- "stream_data": {:hex, :stream_data, "1.1.1", "fd515ca95619cca83ba08b20f5e814aaf1e5ebff114659dc9731f966c9226246", [:mix], [], "hexpm", "45d0cd46bd06738463fd53f22b70042dbb58c384bb99ef4e7576e7bb7d3b8c8c"},
+ "stream_data": {:hex, :stream_data, "1.1.2", "05499eaec0443349ff877aaabc6e194e82bda6799b9ce6aaa1aadac15a9fdb4d", [:mix], [], "hexpm", "129558d2c77cbc1eb2f4747acbbea79e181a5da51108457000020a906813a1a9"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
}