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
4 changes: 2 additions & 2 deletions lib/commanded/commands/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -524,9 +524,9 @@ defmodule Commanded.Commands.Router do

application = Keyword.fetch!(opts, :application)
causation_id = Keyword.get(opts, :causation_id)
command_uuid = Keyword.get_lazy(opts, :command_uuid, &UUID.uuid4/0)
command_uuid = Keyword.get_lazy(opts, :command_uuid, &UUID.uuid7/0)
consistency = Keyword.fetch!(opts, :consistency)
correlation_id = Keyword.get_lazy(opts, :correlation_id, &UUID.uuid4/0)
correlation_id = Keyword.get_lazy(opts, :correlation_id, &UUID.uuid7/0)
metadata = Keyword.fetch!(opts, :metadata) |> validate_metadata()

retry_attempts = Keyword.get(opts, :retry_attempts)
Expand Down
4 changes: 2 additions & 2 deletions lib/commanded/event/mapper.ex
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ defmodule Commanded.Event.Mapper do
%ExampleEvent1{},
%ExampleEvent2{}
],
causation_id: Commanded.UUID.uuid4(),
correlation_id: Commanded.UUID.uuid4(),
causation_id: Commanded.UUID.uuid7(),
correlation_id: Commanded.UUID.uuid7(),
metadata: %{"user_id" => user_id}
)

Expand Down
2 changes: 1 addition & 1 deletion lib/commanded/event_store/adapters/in_memory.ex
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ defmodule Commanded.EventStore.Adapters.InMemory do
} = event

%RecordedEvent{
event_id: event_id || UUID.uuid4(),
event_id: event_id || UUID.uuid7(),
event_number: event_number,
stream_id: stream_uuid,
stream_version: stream_version,
Expand Down
5 changes: 5 additions & 0 deletions lib/commanded/uuid.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,9 @@ defmodule Commanded.UUID do
Generates a UUID using the configured provider (defaults to `Uniq.UUID`).
"""
defdelegate uuid4, to: @uuid_mod

@doc """
Generates a UUIDv7 using the configured provider (defaults to `Uniq.UUID`).
"""
defdelegate uuid7, to: @uuid_mod
end
Comment on lines +20 to 24

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Prevent runtime breakages with custom providers: add uuid7/0 fallback (or update docs and enforce).

Today uuid7/0 is a direct delegate; if a custom provider lacks uuid7/0, calls from the router/in-memory adapter will crash. Suggest a backward-compatible fallback to uuid4/0 when uuid7/0 isn’t exported.

Apply this change:

-  @doc """
-  Generates a UUIDv7 using the configured provider (defaults to `Uniq.UUID`).
-  """
-  defdelegate uuid7, to: @uuid_mod
+  @doc """
+  Generates a UUIDv7 using the configured provider (defaults to `Uniq.UUID`).
+  Falls back to `uuid4/0` if the configured provider does not implement `uuid7/0`.
+  """
+  def uuid7 do
+    if function_exported?(@uuid_mod, :uuid7, 0) do
+      @uuid_mod.uuid7()
+    else
+      @uuid_mod.uuid4()
+    end
+  end

Additionally, the moduledoc still states “Your custom module must provide a uuid4/0 function.” Please update it to reflect uuid7/0 usage or the fallback, e.g.:

@moduledoc """
Configurable UUID generation...

By default uses `Uniq.UUID` (supports uuid1/3/4/5, v6, and v7).

config :commanded, Commanded.UUID, module: MyApp.CustomUUID

Your custom module must provide `uuid4/0`. If it also provides `uuid7/0`,
Commanded will use it; otherwise `uuid7/0` falls back to `uuid4/0`.
"""

Note: The default provider Uniq.UUID already supports uuid7/0. (github.com)

If you prefer strictness over fallback, we can instead keep the delegate and raise early with a clear error if uuid7/0 is missing. Want a patch for that path instead?


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "---- file: lib/commanded/uuid.ex (if present) ----"
if [ -f lib/commanded/uuid.ex ]; then
  sed -n '1,240p' lib/commanded/uuid.ex
else
  echo "lib/commanded/uuid.ex not found"
fi

echo
echo "---- search: occurrences of 'uuid7' ----"
rg -n --hidden --no-ignore -S '\buuid7\b' || true

echo
echo "---- search: defdelegate uuid7 ----"
rg -n --hidden --no-ignore -S 'defdelegate\s+uuid7' || true

echo
echo "---- search: '@moduledoc' and look for 'uuid4' text ----"
rg -n --hidden --no-ignore -S '@moduledoc' lib/commanded/uuid.ex || true
rg -n --hidden --no-ignore -S 'Your custom module must provide' || true
rg -n --hidden --no-ignore -S 'uuid4/0' || true

Length of output: 1887


Prevent runtime breakages with custom providers: add uuid7/0 fallback (or update docs and enforce).

defdelegate uuid7/0 currently delegates directly; if a configured provider doesn't export uuid7/0, callers will crash at runtime (seen in lib/commanded/event/mapper.ex, lib/commanded/commands/router.ex, lib/commanded/event_store/adapters/in_memory.ex). Replace the delegate with a safe fallback and update docs.

Apply this change to lib/commanded/uuid.ex:

-  @doc """
-  Generates a UUIDv7 using the configured provider (defaults to `Uniq.UUID`).
-  """
-  defdelegate uuid7, to: @uuid_mod
+  @doc """
+  Generates a UUIDv7 using the configured provider (defaults to `Uniq.UUID`).
+  Falls back to `uuid4/0` if the configured provider does not implement `uuid7/0`.
+  """
+  def uuid7 do
+    if function_exported?(@uuid_mod, :uuid7, 0) do
+      @uuid_mod.uuid7()
+    else
+      @uuid_mod.uuid4()
+    end
+  end

Also update the moduledoc and guides (lib/commanded/uuid.ex @moduledoc and guides/explanations/commands.md) to reflect the fallback, e.g.:

@moduledoc """
Configurable UUID generation...

By default uses `Uniq.UUID` (supports uuid1/3/4/5, v6, and v7).

config :commanded, Commanded.UUID, module: MyApp.CustomUUID

Your custom module must provide `uuid4/0`. If it also provides `uuid7/0`,
Commanded will use it; otherwise `uuid7/0` falls back to `uuid4/0`.
"""

If you prefer strictness instead of a fallback, keep the delegate and raise early with a clear error — I can provide that patch.