RailsEventStore / rails_event_store

A Ruby implementation of an Event Store based on Active Record
http://railseventstore.org
MIT License
1.4k stars 122 forks source link

Generated migration is not Strong Migrations compatible #1755

Closed thedumbtechguy closed 3 months ago

thedumbtechguy commented 3 months ago

Strong Migrations flags the generated migration as dangerous due to the out of band foreign key and index creation.

The generated migration looks like this

# frozen_string_literal: true

class CreateEventStoreEvents < ActiveRecord::Migration[7.1]
  def change
    create_table(:event_store_events_in_streams, id: :bigserial, force: false) do |t|
      t.string      :stream,      null: false
      t.integer     :position,    null: true
      t.references  :event,       null: false, type: :uuid, index: false
      t.datetime    :created_at,  null: false, type: :timestamp, precision: 6, index: true
    end
    add_index :event_store_events_in_streams, [:stream, :position], unique: true
    add_index :event_store_events_in_streams, [:stream, :event_id], unique: true
    add_index :event_store_events_in_streams, [:event_id]

    create_table(:event_store_events, id: :bigserial, force: false) do |t|
      t.references  :event,       null: false, type: :uuid, index: { unique: true }
      t.string      :event_type,  null: false, index: true
      t.jsonb      :metadata
      t.jsonb      :data, null: false
      t.datetime    :created_at,  null: false, type: :timestamp, precision: 6, index: true
      t.datetime    :valid_at,    null: true, type: :timestamp, precision: 6, index: true
    end

    add_foreign_key "event_store_events_in_streams", "event_store_events", column: "event_id", primary_key: "event_id"
  end
end

I modified mine to this to get it to work correctly.

# frozen_string_literal: true

class CreateEventStoreEvents < ActiveRecord::Migration[7.1]
  def change
    create_table(:event_store_events, id: :bigserial, force: false) do |t|
      t.references :event, null: false, type: :uuid, index: {unique: true}
      t.string :event_type, null: false, index: true
      t.jsonb :metadata
      t.jsonb :data, null: false
      t.datetime :created_at, null: false, type: :timestamp, precision: 6, index: true
      t.datetime :valid_at, null: true, type: :timestamp, precision: 6, index: true
    end

    create_table(:event_store_events_in_streams, id: :bigserial, force: false) do |t|
      t.string :stream, null: false
      t.integer :position, null: true
      t.references :event, null: false, type: :uuid, index: false, foreign_key: {to_table: :event_store_events, primary_key: :event_id}
      t.datetime :created_at, null: false, type: :timestamp, precision: 6, index: true

      t.index [:stream, :position], unique: true
      t.index [:stream, :event_id], unique: true
      t.index [:event_id]
    end
  end
end

My schema.rb looks this


  create_table "event_store_events", force: :cascade do |t|
    t.uuid "event_id", null: false
    t.string "event_type", null: false
    t.binary "metadata"
    t.binary "data", null: false
    t.datetime "created_at", null: false
    t.datetime "valid_at"
    t.index ["created_at"], name: "index_event_store_events_on_created_at"
    t.index ["event_id"], name: "index_event_store_events_on_event_id", unique: true
    t.index ["event_type"], name: "index_event_store_events_on_event_type"
    t.index ["valid_at"], name: "index_event_store_events_on_valid_at"
  end

  create_table "event_store_events_in_streams", force: :cascade do |t|
    t.string "stream", null: false
    t.integer "position"
    t.uuid "event_id", null: false
    t.datetime "created_at", null: false
    t.index ["created_at"], name: "index_event_store_events_in_streams_on_created_at"
    t.index ["event_id"], name: "index_event_store_events_in_streams_on_event_id"
    t.index ["stream", "event_id"], name: "index_event_store_events_in_streams_on_stream_and_event_id", unique: true
    t.index ["stream", "position"], name: "index_event_store_events_in_streams_on_stream_and_position", unique: true
  end

  add_foreign_key "event_store_events_in_streams", "event_store_events", column: "event_id", primary_key: "event_id"
thedumbtechguy commented 3 months ago

Another thing I did was use gin indexes. But that may not fit the library.

image
mostlyobvious commented 3 months ago

Thank you for pointing this out! Next RES release should include migration templates compatible with strong_migrations (#1759)

As for the GIN indexes, I've extracted it to a separate issue — it has been on the roadmap already but a little more visibility won't hurt.