Changelog
View SourceAll notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
[2.0.0] - 2026-05-10
The 2.0 line introduces pluggable backends and multi-repo support. The intermediate 2.1-2.5 dev tags were rolled into this release; nothing shipped to Hex during that window. See MIGRATION-2.0.md for upgrade notes.
Added
Pluggable backends. Pick a backend package and point kura at it with
{backend, ...}. Kura auto-populatesdialect,pool_module, anddriver_modulefrom the aggregator at app start.kura_poolbehaviour - connection-pool contract.kura_capabilitiesbehaviour - backend feature flags (e.g.advisory_locks,returning,arrays).kura_dialectbehaviour - SQL dialect contract, with optionalcolumn_type/1andformat_default/1callbacks for backend-specific DDL.kura_driverbehaviour - query/transaction driver contract.kura_pool_ets- in-memory pool for tests that don't need a real DB.
Multi-repo configuration. Run two or more repos in the same app, each with its own backend, dialect, and pool. Configure with a map of maps under
{repos, ...}:{kura, [ {repos, #{ primary => #{ backend => kura_backend_postgres, host => "primary.example.com", database => "main", pool_size => 10 }, analytics => #{ backend => kura_backend_sqlite, database => <<":memory:">> } }} ]}Kura starts each repo's pool on app start. The dialect resolves per-repo, so
primaryemits Postgres SQL whileanalyticsemits SQLite SQL. The query cache is keyed per repo so dialects never share entries.kura_app:pool_config/1returns the config map for a specific repo.kura_types:cast(boolean, 0|1)so SQLite (booleans stored as INTEGER) round-trips cleanly.
Changed
kura_query_compiler:to_sql/2,to_sql_cached/2,to_sql_from/3,insert/4,insert/5,update/5,delete/4,update_all/3,delete_all/2,insert_all/4,insert_all/5, anddialect/1takeRepoModas the first argument so they can resolve the per-repo dialect. Internal callers (kura_repo_worker,kura_stream,kura_migrator) thread the repo through automatically.kura_query_cachekeys are tuples{RepoMod, QueryHash}. The cache ETS table is now owned by a supervised gen_server so it survives any caller exiting.kura_db:query/3and the rest of the query path route throughkura_pool/kura_driver, so backends are swappable.kura_app:pool_config/0is dialect-aware. PG-shaped defaults (port 5432, user/database"postgres", decode_opts) only apply whendialectiskura_dialect_pg. Other dialects get a pass-through map of user-set kura env keys.kura_app:configure_pg_types/0only runs whendialectiskura_dialect_pg.kura_repo:read_config/1returns a pass-through map of all kura env keys (minus bookkeeping). Previously hardcoded the PG-shaped subset and silently droppedpool_module,driver_module,backend.kura_migratorskipspg_advisory_xact_lockwhen the configured pool doesn't declare theadvisory_lockscapability. The migration transaction alone serializes runs on backends that lack advisory locks (SQLite, etc.).- Single-repo legacy config (flat
kuraenv keys) and the per-app form (application:get_env(OtpApp, RepoMod)) keep working unchanged. The globalapplication:get_env(kura, dialect)is consulted as a fallback when a repo's config map omitsdialect.
Removed (BREAKING)
kura_pool_pgo,kura_driver_pgono longer ship inkura. Installkura_postgresfor Postgres orkura_sqlitefor SQLite.pgois no longer a runtime dependency ofkura.kura_dialect_pgremains in core as the default ANSI-ish SQL emitter that other dialects delegate to.kura_query_compiler:dialect/0no longer defaults tokura_dialect_pg. Usedialect/1and configure the dialect per repo (recommended via{backend, ...}), or set the globalapplication:set_env(kura, dialect, Module)for legacy single-dialect setups.kura_db:get_pool_module/1andget_driver_module/1error with{no_pool_module_configured, _}/{no_driver_module_configured, _}when not configured. Set{backend, ...}(recommended) orpool_module/driver_moduleexplicitly.
[1.8.0] - 2026-03-06
Changed
- Breaking:
kura_repobehaviour now requiresotp_app/0callback instead ofconfig/0- database configuration is read from application environment viaapplication:get_env(OtpApp, RepoModule) - Optional
init/1callback onkura_repo- modify config at runtime (read secrets from env vars, files, external services) kura_repo:config/1function reads config from app env, then callsinit/1if defined- All internal
get_pool/1calls now usekura_repo:config/1instead ofRepoMod:config()
[1.7.0] - 2026-03-06
Added
telemetrylibrary integration - every query emits a[kura, repo, query]telemetry event with measurements (duration,duration_us) and metadata (query,params,repo,result,num_rows,source)build_telemetry_metadata/4andextract_source/1exported fromkura_repo_workerfor testingsourcefield in telemetry metadata - table name extracted from the SQL query
Changed
- Legacy
{kura, [{log, ...}]}config still works but now runs alongside telemetry events (not instead of)
[1.6.0] - 2026-03-06
Added
- Ecto-style index support -
indexes/0optional callback onkura_schemafor declaring indexes at the schema level - Map-based
{create_index, Table, Cols, Opts}migration operation with auto-generated index names ({table}_{cols}_index) kura_migration:index_name/2helper for generating Ecto-style index names- Unique indexes declared via
indexes/0auto-register changeset constraints - no manualunique_constraint/3calls needed - New types:
index_def(),index_opts_map()inkura_migration
[1.5.0] - 2026-03-05
Added
- Table-level constraints in migrations -
{create_table, Table, Columns, Constraints}4-tuple variant supporting{unique, [atom()]}and{check, binary()}inline constraints constraints/0optional callback onkura_schemabehaviour - declares table-level constraints on the schema- Auto-registration of schema constraints on
kura_changeset:cast/4- duplicate inserts get friendly error messages without manualunique_constraint/3calls
[1.4.0] - 2026-03-05
Changed
- Breaking: Minimum OTP version is now OTP 28 (sigil syntax, ELP fixes)
- Binary string literals converted to sigil syntax (
~"...") across changeset modules - Eliminated unnecessary intermediate list and tuple allocations in association casting
[1.3.2] - 2026-03-05
Fixed
Dialyzer warnings in
get_field/2,3-maps:find/2returns{ok, V} | error, not map patterns- Atom exhaustion risk in enum
load/2- now validates against declared values list instead ofbinary_to_atom - Atom exhaustion risk in
kura_repo_workererror handlers - NOT NULL and constraint errors fall back tobaseatom for unknown columns - CI running resilience tests that require docker compose control - explicitly list test modules
Added
- Production readiness test suites:
kura_stress_tests(7 concurrency tests),kura_bench_tests(6 throughput benchmarks),kura_migration_stress_tests(4 migration safety tests),kura_resilience_tests(4 failure/recovery tests) - Makefile targets:
test-stress,test-bench,test-migration-stress,test-resilience,test-production
[1.3.1] - 2026-03-04
Fixed
- Advisory lock query wrapped to avoid pgo void type decode error (
pg_advisory_xact_lockreturns void)
[1.3.0] - 2026-03-03
Changed
- Primary key derived from field flag instead of separate callback
[1.2.0] - 2026-03-01
Added
- Changeset validators:
validate_exclusion,validate_subset,traverse_errors,prepare_changes,optimistic_lock - Query extensions: subqueries in WHERE, CTEs, UNION/INTERSECT/EXCEPT, window functions (
select_expr), query scopes kura_sandbox- test transaction rollback for isolated concurrent testskura_stream- server-side cursor batching for large result setsinsert_allwith RETURNING support
Changed
to_sql_from/2refactored as keystone for all query compilation
[1.1.0] - 2026-02-27
Added
- Foreign key constraints in migrations -
references,on_delete,on_updatefields on#kura_column{}generate inlineREFERENCES "table"("col") ON DELETE CASCADEetc. kura_changeset:validate_confirmation/2,3- validates a field has a matching<field>_confirmationin params (for password/email confirmation flows)kura_repo_worker:exists/2- checks if any record matches a query, returns{ok, true | false}kura_repo_worker:reload/3- re-fetches a record from the database by its primary key
[1.0.4] - 2026-02-26
Fixed
- ExDoc build failure -
doc/architecture.mdwas wiped by edoc before ex_doc could read it; moved toguides/architecture.md - Removed
docfrom hex include_files (generated output, not source)
Added
- ExDoc build step in CI to catch documentation errors early
[1.0.3] - 2026-02-26
Added
- Rolling deployment safety warnings - detect dangerous migration operations (
drop_column,rename_column,modify_column,drop_table,add_column NOT NULLwithout default) and log warnings before execution - Optional
safe/0callback onkura_migrationbehaviour to suppress warnings when the expand-contract pattern has been followed - Rolling deployments guide (
guides/rolling_deployments.md) documenting the expand-contract pattern with step-by-step examples
[1.0.2] - 2026-02-24
Added
kura_preloadermodule - extracted preload logic fromkura_repo_workerinto a dedicated modulekura_changeset_assocmodule - extractedcast_assoc,put_assoc, and association coercion fromkura_changeset- Comprehensive integration tests (356 total) covering all query operators, aggregates, joins, bulk ops, multi pipelines, on_conflict upserts, constraint errors, type round-trips, and all association/preload types
Fixed
- Constraint error handling (
unique_constraint,foreign_key_constraint,check_constraint) now correctly unwraps pgo{pgsql_error, Map}tuples put_assocfor many_to_many now preserves primary key in changeset data
[1.0.1] - 2026-02-22
Added
- Embedded schemas guide (
guides/embedded-schemas.md) - Architecture doc linked in hexdocs extras
- Logo displayed in hexdocs
- Navigation groups: Getting Started, Guides, Reference
Changed
- Hidden internal modules (
kura_app,kura_sup) from docs
[1.0.0] - 2026-02-22
Changed
- Consolidated CI and release into a single GitHub Actions workflow
- Updated README features list to reflect all capabilities added since 0.2.0
- Fixed stale
log_queriesconfig reference in README (nowlog) - Fixed stale migration discovery docs in README (module-based since 0.3.0)
[0.5.0] - 2026-02-14
Added
- Embedded schemas (
embeds_one,embeds_many) stored as JSONB withcast_embed/2,3 - Many-to-many associations with
join_through/join_keys, preloading, and persistence viacast_assoc/put_assoc - Schemaless changesets -
cast/4accepts a types map for validation-only workflows Makefilefor local test lifecycle management (make testwith Docker Compose)- Schemaless changesets guide (
guides/schemaless-changesets.md)
[0.4.1] - 2026-02-14
Added
- Hex documentation guides for enums, telemetry, and cast_assoc
[0.4.0] - 2026-02-14
Added
- Enum type (
{enum, [atom()]}) - stored asVARCHAR(255), cast/dump/load between atoms and binaries Query telemetry via
sys.config-{kura, [{log, true | {M,F}}]}with timing on every querykura_changeset:cast_assoc/2,3- nested changeset casting forhas_manyandhas_oneassociationskura_changeset:put_assoc/3- directly attach maps or pre-built changesets as association changesassoc_changesfield on#kura_changeset{}record- Nested insert/update - automatic transaction wrapping with FK propagation when
assoc_changespresent kura_repo_worker:build_log_event/5anddefault_logger/0exported for testability- Architecture documentation (
guides/architecture.md) - Feature guides: enums, telemetry, cast_assoc (
guides/)
Changed
- Telemetry reads from
application:get_env(kura, log)instead of repoconfig/0map - Removed
log_queriesapplication env default (replaced bylog) kura_repo_worker:insert/2andupdate/2refactored to support nested association persistence
Removed
maybe_log/2internal function (replaced by centralizedemit_log/5)
[0.3.0] - 2026-02-14
Added
- Module-based migration discovery (no filesystem scanning)
- Xref checks in CI
- rebar3_kura plugin reference in README
Removed
migration_pathsconfiguration (migrations now discovered automatically from compiled modules)
[0.2.0] - 2025-02-14
Added
- Constraint declarations (
unique_constraint,foreign_key_constraint,check_constraint) with automatic PostgreSQL error mapping kura_multifor atomic transaction pipelines with insert/update/delete/run steps- Association support (
belongs_to,has_one,has_many) with preloading - Nested preloads
- Standalone preloading via
kura_repo_worker:preload/4 - Query preloads via
kura_query:preload/2 - Upsert support (
insert/3withon_conflictoption) - Bulk operations:
insert_all/3,update_all/3,delete_all/2 kura_changeset:validate_change/3for custom validation functionskura_changeset:apply_action/2- Module documentation (
-moduledoc/-doc) for all public modules - Usage guides: Getting Started, Changesets, Query Builder, Associations, Multi, Migrations
- CI/CD with GitHub Actions
- CHANGELOG
[0.1.0] - 2025-01-15
Added
kura_schemabehaviour for defining database-backed schemaskura_changesetfor casting, validation, and change trackingkura_querycomposable query builderkura_query_compilerfor parameterized SQL generationkura_repobehaviour andkura_repo_workerfor CRUD operationskura_typestype system (id, integer, float, string, text, boolean, date, utc_datetime, uuid, jsonb, arrays)kura_migrationbehaviour andkura_migratorfor DDL migrations- Automatic timestamps (
inserted_at,updated_at) - Query logging via application env