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-2
0.8.2
fixed
Fixed
use ActiveMemory.Storeanduse ActiveMemory.ActiveRepono longer swallow the using module's ownhandle_infoclauses. The injected unknown-message catch-all is now added via@before_compile— after user-defined clauses — instead of at theusesite above them, where it made every custom clause unreachable: timer ticks and monitor messages sent to a store were silently discarded, and Elixir 1.19's type checker flags the dead clauses as redundant. Library-owned messages (:sweep,:"ETS-TRANSFER") keep priority; unknown messages still no-op.
0-8-1
0.8.1
added
Added
mix active_memory.candidates— reads table statistics through the application's own Ecto repo (PostgreSQL viapg_stat_user_tables, MySQL/MariaDB viaperformance_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-rowsand--min-reads, and--timeoutcovers 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/3in anattributesblock takes an optional Ecto type —field :name, :string— defaulting to:any. Types are not enforced on write; they powerEcto.Changesetcasting and validation. Available as__attributes__(:types).- A
Tablecan skipattributesentirely and define an Ectoembedded_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_idprimary key, a custom key such as@primary_key {:uuid, Ecto.UUID, autogenerate: true}, andtimestamps(). write/1accepts anEcto.Changesetas well as a struct, likeEcto.Repo.insert/2. An invalid changeset returns{:error, changeset}with itsactionset 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/1andreload!/1. The bang variants raiseActiveMemory.NotFoundError.count/1, which asks the table for its size rather than copying records out, so it is O(1). On attltable it takessweep: trueto exclude expired but unswept records.exists?/2.:order_by,:limitand:offsetonall/1andselect/2. Sorting happens after reading, and uses a value's owncompare/2when it has one, soDecimaland the calendar types order correctly rather than by Erlang term order.ActiveMemory.NotFoundErrorandActiveMemory.MultipleResultsError.__attributes__(:primary_key), the table's first field — what ETS and Mnesia key a record on.
changed-1
Changed
one/1now raisesActiveMemory.MultipleResultsErrorwhen a query matches more than one record, instead of returning{:error, :more_than_one_result}. This matchesEcto.Repo.one/2.withdraw/1raises 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-1
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 anilkey 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_copiesplusdisc_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 — raisesArgumentError, since it would fail on every boot. - Compile warnings from the test dependency
local_cluster(deprecated:slaveusage, charlist syntax) are gone with the upgrade to 2.1.
documentation
Documentation
- A "Deleting a record" section explaining that
delete/1matches a record in full, that a stale struct therefore removes nothing while still returning:ok, and thatwithdraw/1is the query based alternative. - A
ttlon an Ecto schema table needs an explicitfield :expires_at, :integer; theattributesDSL still adds it for you. StoreandActiveRepodocumentation 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 needasync: false, namely modules sharing the application's store, since a table's module name is its physical table name. - The
majorityoption, 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-2
Fixed
use ActiveMemory.Storeanduse ActiveMemory.ActiveReporead the table'sttlwhile 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 — raisedUndefinedFunctionErroron code that compiles and runs correctly. The lookup happens at runtime now.
0-7-3
0.7.3
fixed-3
Fixed
- Removed an unreachable
ttlclause that newer Elixir type checkers flagged in any project defining a store on a table without attl. - Removed unreachable clauses in the ETS adapter's
select/2.
0-7-2
0.7.2
changed-2
Changed
delete_all/0returns:okon success. The ETS adapter had returned the rawtruefrom:ets.delete_all_objects/1and the Mnesia adapter{:atomic, :ok}, neither matching the documented:ok | {:error, any()}spec, so:ok = Store.delete_all()raised aMatchError.
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.