View Source Changelog

All notable changes to ActiveMemory are documented here. Versions follow Semantic Versioning; while the package is pre-1.0 a minor bump may carry a behavior change, and those are called out below.

0-8-1

0.8.1

added

Added

  • mix active_memory.candidates — reads table statistics through the application's own Ecto repo (PostgreSQL via pg_stat_user_tables, MySQL/MariaDB via performance_schema) and reports each table's read/write ratio, row count and size, flagging the high-read, low-write tables that are candidates for an ActiveMemory table. Thresholds are tunable with --min-ratio, --max-rows and --min-reads, and --timeout covers schemas whose statistics views are slow to answer.

    The task does not start your application: only its configuration is loaded, and the one repo you name is started with a two connection pool, so it is safe to point at a production database with read-only credentials. It runs only read-only queries against statistics views, never application tables. Known infrastructure tables (job queues, migration bookkeeping) are reported as such rather than as candidates, and tables with too little recorded traffic are not judged at all.

    Validated against PostgreSQL and against a production Aurora MySQL database.

changed

Changed

  • ActiveMemory now requires Elixir 1.15 or later, matching the versions CI tests against. Earlier Elixirs may continue to work but are no longer resolved for or verified.

0-8-0

0.8.0

Ecto compatibility. A Table can now be typed or defined as an Ecto schema, and the read API covers what Ecto.Repo offers.

added-1

Added

  • field/3 in an attributes block takes an optional Ecto type — field :name, :string — defaulting to :any. Types are not enforced on write; they power Ecto.Changeset casting and validation. Available as __attributes__(:types).
  • A Table can skip attributes entirely and define an Ecto embedded_schema. All table metadata is derived from the schema, so the module is a real Ecto schema and every changeset function works on it.
  • Autogenerated schema fields are honored on write: an :id/:binary_id primary key, a custom key such as @primary_key {:uuid, Ecto.UUID, autogenerate: true}, and timestamps().
  • write/1 accepts an Ecto.Changeset as well as a struct, like Ecto.Repo.insert/2. An invalid changeset returns {:error, changeset} with its action set to :insert, which is what a Phoenix form needs to render errors.
  • get/1, get!/1, get_by/1, get_by!/1, one!/1, reload/1 and reload!/1. The bang variants raise ActiveMemory.NotFoundError.
  • count/1, which asks the table for its size rather than copying records out, so it is O(1). On a ttl table it takes sweep: true to exclude expired but unswept records.
  • exists?/2.
  • :order_by, :limit and :offset on all/1 and select/2. Sorting happens after reading, and uses a value's own compare/2 when it has one, so Decimal and the calendar types order correctly rather than by Erlang term order.
  • ActiveMemory.NotFoundError and ActiveMemory.MultipleResultsError.
  • __attributes__(:primary_key), the table's first field — what ETS and Mnesia key a record on.

changed-1

Changed

  • one/1 now raises ActiveMemory.MultipleResultsError when a query matches more than one record, instead of returning {:error, :more_than_one_result}. This matches Ecto.Repo.one/2. withdraw/1 raises there too, since deleting an arbitrary one of several matches would be wrong. Callers matching on {:error, :more_than_one_result} need updating.
  • An Ecto schema table that declares a primary key somewhere other than its first field, or a composite primary key, raises when the table is created. An in memory table keys a record on its first field, so neither can be honored, and the previous behavior would have read the wrong field.

fixed

Fixed

  • An Ecto schema table with an autogenerating primary key of a custom type (@primary_key {:uuid, Ecto.UUID, autogenerate: true}) generated no key, so every record was written under a nil key and collapsed onto one entry.
  • Reconciling a recovered Mnesia table's options could crash the store's startup: an option change Mnesia refused raised out of init/1, and a table naming more than one copy type (ram_copies plus disc_copies, for instance) crashed the reconciliation even when the configuration was valid. A refused change is now logged as a warning and skipped, so the store starts and the table keeps its current setting. A copy configuration that is wrong in the code itself — the same node under two copy types — raises ArgumentError, since it would fail on every boot.
  • Compile warnings from the test dependency local_cluster (deprecated :slave usage, charlist syntax) are gone with the upgrade to 2.1.

documentation

Documentation

  • A "Deleting a record" section explaining that delete/1 matches a record in full, that a stale struct therefore removes nothing while still returning :ok, and that withdraw/1 is the query based alternative.
  • A ttl on an Ecto schema table needs an explicit field :expires_at, :integer; the attributes DSL still adds it for you.
  • Store and ActiveRepo documentation now describes the same behavior for the operations they share.
  • A "Testing" section covering which test modules can run async: true — a module owning its own table and store runs concurrently today — and which need async: false, namely modules sharing the application's store, since a table's module name is its physical table name.
  • The majority option, Mnesia's quorum writes, is documented as the mitigation for partition behavior rather than one line in a list of options.

0-7-4

0.7.4

fixed-1

Fixed

  • use ActiveMemory.Store and use ActiveMemory.ActiveRepo read the table's ttl while the caller's module compiled, making every table a compile time dependency of its store. Tooling that compiles a store's file without the table module loaded — a language server, for one — raised UndefinedFunctionError on code that compiles and runs correctly. The lookup happens at runtime now.

0-7-3

0.7.3

fixed-2

Fixed

  • Removed an unreachable ttl clause that newer Elixir type checkers flagged in any project defining a store on a table without a ttl.
  • Removed unreachable clauses in the ETS adapter's select/2.

0-7-2

0.7.2

changed-2

Changed

  • delete_all/0 returns :ok on success. The ETS adapter had returned the raw true from :ets.delete_all_objects/1 and the Mnesia adapter {:atomic, :ok}, neither matching the documented :ok | {:error, any()} spec, so :ok = Store.delete_all() raised a MatchError.

0-7-1-and-earlier

0.7.1 and earlier

See the commit history for releases before this changelog was kept. Notable additions were record expiry (ttl), ActiveMemory.ActiveRepo for managing several tables from one process, ActiveMemory.TableHeir so an ETS table survives its store crashing, and making withdraw/1 a single atomic operation.