Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions guides/explanations/fork-differences.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,49 @@ This fork maintains an independent release cycle to introduce new features and i
- Update dependency to `{:commanded, "~> 2.1"}` with `{:ecto, "~> 3.11"}`
- Update config from `config :commanded_ecto_projections, repo: MyApp.Repo` to `config :commanded, Commanded.Projections.Ecto, repo: MyApp.Repo`
- No code changes required - API remains the same

### **Timezone-Aware Timestamps for Projections**

**Changes:**
- `projection_versions` table now uses `timestamp with time zone` for `inserted_at` and `updated_at` columns
- ProjectionVersion schema updated to use `:utc_datetime_usec` instead of `:naive_datetime_usec`
- Added migration helper modules for both new and existing installations

**Benefits:**
- Ensures timestamp values are stored with timezone information
- Better consistency across different database configurations
- Prevents timezone-related issues when working with distributed systems

**For new installations (greenfield):**
```elixir
defmodule MyApp.Repo.Migrations.CreateProjectionVersionsTable do
alias Commanded.Projections.Ecto.Migrations.V01CreateProjectionVersionsTable

use Ecto.Migration

def up do
V01CreateProjectionVersionsTable.up()
end

def down do
V01CreateProjectionVersionsTable.down()
end
end
```

**For existing installations (upgrade):**
```elixir
defmodule MyApp.Repo.Migrations.UpgradeProjectionVersionsTimestamps do
alias Commanded.Projections.Ecto.Migrations.V02UpgradeTimestampsToTimezone

use Ecto.Migration

def up do
V02UpgradeTimestampsToTimezone.up()
end

def down do
V02UpgradeTimestampsToTimezone.down()
end
end
```
13 changes: 4 additions & 9 deletions guides/howtos/building-read-models-with-ecto.md
Original file line number Diff line number Diff line change
Expand Up @@ -453,22 +453,17 @@ When using a prefix for your Ecto schemas you might also want to change the pref

```elixir
defmodule CreateSchemaProjectionVersions do
alias Commanded.Projections.Ecto.Migrations.V01CreateProjectionVersionsTable

use Ecto.Migration

def up do
execute("CREATE SCHEMA example_schema_prefix")

create table(:projection_versions, primary_key: false, prefix: "example_schema_prefix") do
add(:projection_name, :text, primary_key: true)
add(:last_seen_event_number, :bigint)

timestamps(type: :naive_datetime_usec)
end
V01CreateProjectionVersionsTable.up(prefix: "example_schema_prefix")
end

def down do
drop(table(:projection_versions, prefix: "example_schema_prefix"))

V01CreateProjectionVersionsTable.down(prefix: "example_schema_prefix")
execute("DROP SCHEMA example_schema_prefix CASCADE")
end
end
Expand Down
13 changes: 7 additions & 6 deletions guides/howtos/ecto-projections-getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@ Commanded includes built-in support for read model projections using Ecto. You s

```elixir
defmodule CreateProjectionVersions do
alias Commanded.Projections.Ecto.Migrations.V01CreateProjectionVersionsTable

use Ecto.Migration

def change do
create table(:projection_versions, primary_key: false) do
add(:projection_name, :text, primary_key: true)
add(:last_seen_event_number, :bigint)
def up do
V01CreateProjectionVersionsTable.up()
end

timestamps(type: :naive_datetime_usec)
end
def down do
V01CreateProjectionVersionsTable.down()
end
end
```
Expand Down
2 changes: 1 addition & 1 deletion lib/commanded/projections/ecto.ex
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ if Code.ensure_loaded?(Ecto) do
schema "projection_versions" do
field(:last_seen_event_number, :integer)

timestamps(type: :naive_datetime_usec)
timestamps(type: :utc_datetime_usec)
end
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
if Code.ensure_loaded?(Ecto.Migration) do
defmodule Commanded.Projections.Ecto.Migrations.V01CreateProjectionVersionsTable do
@moduledoc """
Version 1: Creates the `projection_versions` table with timezone-aware timestamps.

Use this migration for **new installations** (greenfield projects).

## Usage

defmodule MyApp.Repo.Migrations.CreateProjectionVersionsTable do
use Ecto.Migration

def up do
Commanded.Projections.Ecto.Migrations.V01CreateProjectionVersionsTable.up()
end

def down do
Commanded.Projections.Ecto.Migrations.V01CreateProjectionVersionsTable.down()
end
end

## Options

* `:prefix` - The database schema prefix. Defaults to `nil` (public schema).

## Examples

# Default schema (public)
Commanded.Projections.Ecto.Migrations.V01CreateProjectionVersionsTable.up()

# Custom schema
Commanded.Projections.Ecto.Migrations.V01CreateProjectionVersionsTable.up(prefix: "projections")

"""

use Ecto.Migration

def up(opts \\ []) do
prefix = Keyword.get(opts, :prefix)

create table(:projection_versions, primary_key: false, prefix: prefix) do
add(:projection_name, :text, primary_key: true)
add(:last_seen_event_number, :bigint)

timestamps(type: :timestamptz)
end
end

def down(opts \\ []) do
prefix = Keyword.get(opts, :prefix)
drop(table(:projection_versions, prefix: prefix))
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
if Code.ensure_loaded?(Ecto.Migration) do
defmodule Commanded.Projections.Ecto.Migrations.V02UpgradeTimestampsToTimezone do
@moduledoc """
Version 2: Upgrades timestamps to timezone-aware types.

Use this migration for **existing installations** that have the old schema without timezone-aware timestamps.

## Usage

defmodule MyApp.Repo.Migrations.UpgradeProjectionVersionsTimestamps do
use Ecto.Migration

def up do
Commanded.Projections.Ecto.Migrations.V02UpgradeTimestampsToTimezone.up()
end

def down do
Commanded.Projections.Ecto.Migrations.V02UpgradeTimestampsToTimezone.down()
end
end

## Options

* `:prefix` - The database schema prefix. Defaults to `nil` (public schema).

## Examples

# Default schema (public)
Commanded.Projections.Ecto.Migrations.V02UpgradeTimestampsToTimezone.up()

# Custom schema
Commanded.Projections.Ecto.Migrations.V02UpgradeTimestampsToTimezone.up(prefix: "projections")

"""

use Ecto.Migration

def up(opts \\ []) do
prefix = Keyword.get(opts, :prefix)

alter table(:projection_versions, prefix: prefix) do
modify(:inserted_at, :timestamptz, from: :naive_datetime_usec)
modify(:updated_at, :timestamptz, from: :naive_datetime_usec)
end
end

def down(opts \\ []) do
prefix = Keyword.get(opts, :prefix)

alter table(:projection_versions, prefix: prefix) do
modify(:inserted_at, :naive_datetime_usec, from: :timestamptz)
modify(:updated_at, :naive_datetime_usec, from: :timestamptz)
end
end
end
end
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
defmodule Commanded.Projections.Repo.Migrations.CreateProjectionVersions do
alias Commanded.Projections.Ecto.Migrations.V01CreateProjectionVersionsTable

use Ecto.Migration

def change do
create table(:projection_versions, primary_key: false) do
add(:projection_name, :text, primary_key: true)
add(:last_seen_event_number, :bigint)
def up do
V01CreateProjectionVersionsTable.up()
end

timestamps(type: :naive_datetime_usec)
end
def down do
V01CreateProjectionVersionsTable.down()
end
end
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
defmodule Commanded.Projections.Repo.Migrations.CreateProjectionVersionWithPrefix do
alias Commanded.Projections.Ecto.Migrations.V01CreateProjectionVersionsTable

use Ecto.Migration

def up do
execute("CREATE SCHEMA test")

create table(:projection_versions, primary_key: false, prefix: "test") do
add(:projection_name, :text, primary_key: true)
add(:last_seen_event_number, :bigint)

timestamps(type: :naive_datetime_usec)
end
V01CreateProjectionVersionsTable.up(prefix: "test")
end

def down do
drop(table(:projection_versions, prefix: "test"))

V01CreateProjectionVersionsTable.down(prefix: "test")
execute("DROP SCHEMA test CASCADE")
end
end
2 changes: 1 addition & 1 deletion test/test_helper.exs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ defmodule CreateProjections do
end
end

Ecto.Migrator.up(Repo, 20_170_609_120_000, CreateProjections)
Ecto.Migrator.up(Repo, 20_171_001_000_000, CreateProjections)

ExUnit.start()

Expand Down
Loading