Рельсы 6 пессимисти c замок с JSONB - PullRequest
2 голосов
/ 26 марта 2020

У меня есть выделенная служба, используемая для pu sh на отметках времени столбца jsonb о некоторых событиях.

Конфигурация:

pg: 1.2.2
PostgreSQL: 11.4
Rails: 6.0.2.1
Ruby: 2.6.5

Вот схема моей таблицы:

create_table "invitations", force: :cascade do |t|
  t.uuid "uuid", default: -> { "gen_random_uuid()" }, null: false
  t.datetime "created_at", precision: 6, null: false
  t.datetime "updated_at", precision: 6, null: false
  t.jsonb "notification_statuses", default: {}, null: false
end

Вот типичная jsonb У меня есть для столбца notification_statuses:

{emails: [{created_at: "some date", sent_at: "some date2"},
          {created_at: "some other date", sent_at: "some other date2"}]}

Вот код:

class MyNotificationManager

  def initialize(invitation_id)
    @invitation = Invitation.find(invitation_id)
  end

  def start_new_notification_status(type)
    @invitation.with_lock do
      @invitation.notification_statuses[type] = [] unless @invitation.notification_statuses.key?(type)
      hsh = { created_at: Time.zone.now, sent_at: nil }
      @invitation.notification_statuses[type].push(hsh)
      @invitation.save!
      puts "push #{@invitation.notification_statuses[type].count}"
    end
  end
end

Вот тест:

describe '#start_new_notification_status' do
  subject { MyNotificationManager }

  describe "with concurrency" do
    let(:concurrency_level) { 4 }
    let!(:invitation) { create(:invitation) }

    it "updates invitation one at the time" do
      expect(ActiveRecord::Base.connection.pool.size).to eq(5)
      fail_occurred = false
      wait_for_it = true

      threads = concurrency_level.times.map do |i|
        Thread.new do
          true while wait_for_it
          begin
            serv = subject.new(invitation.id)
            serv.start_new_notification_status('emails')
          rescue StandardError => err
            fail_occurred = true
          end
        end
      end
      wait_for_it = false
      threads.each(&:join)
      expect(fail_occurred).not_to be_truthy
      expect(invitation.reload.notification_statuses['emails'].count).to eq(concurrency_level)
    end
  end
end

Наконец, мой RSpe c logs:

Event::NotificationManager
  #start_new_notification_status
    with concurrency
push 1
push 2
push 2
push 2

     Failure/Error: expect(invitation.reload.notification_statuses['emails'].count).to eq(concurrency_level)

       expected: 4
            got: 2

Чего мне не хватает? Журналы, очевидно, должны быть:

Event::NotificationManager
  #start_new_notification_status
    with concurrency
push 1
push 2
push 3
push 4

Вот мои журналы.

   (0.5ms)  SELECT "ar_internal_metadata"."value" FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1  [["key", "schema_sha1"]]
   (0.7ms)  SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
   (0.9ms)  BEGIN
   (0.3ms)  SAVEPOINT active_record_1
  Address Create (19.8ms)  INSERT INTO "addresses" ("uuid", "line1", "line2", "locality", "zip_postcode", "country_province", "country", "latitude", "longitude", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING "id"  [["uuid", "6ca512f7-aa9d-4deb-9fce-7ce3e878f5ee"], ["line1", "27862 Kautzer Dam"], ["line2", "Apt. 831"], ["locality", "West Gladys"], ["zip_postcode", "27812-2854"], ["country_province", "North Carolina"], ["country", "Saint Kitts and Nevis"], ["latitude", 6.990987893090221], ["longitude", -79.50793749705787], ["created_at", "2020-04-20 07:49:21.760116"], ["updated_at", "2020-04-20 07:49:21.760116"]]
   (0.3ms)  RELEASE SAVEPOINT active_record_1
   (0.3ms)  SAVEPOINT active_record_1
  Establishment Create (14.0ms)  INSERT INTO "establishments" ("name", "contact_name", "description", "note", "insta_user_name", "address_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING "id"  [["name", "Stehr-Donnelly"], ["contact_name", "Narcisa"], ["description", "Asperiores voluptates consequuntur."], ["note", "Dolore qui."], ["insta_user_name", "berta.king"], ["address_id", 1], ["created_at", "2020-04-20 07:49:21.792061"], ["updated_at", "2020-04-20 07:49:21.792061"]]
  ActsAsTaggableOn::Tagging Load (0.6ms)  SELECT "taggings".* FROM "taggings" WHERE "taggings"."taggable_id" = $1 AND "taggings"."taggable_type" = $2  [["taggable_id", 1], ["taggable_type", "Establishment"]]
   (0.2ms)  RELEASE SAVEPOINT active_record_1
   (0.3ms)  SAVEPOINT active_record_1
   (1.0ms)  SELECT COUNT(*) FROM "events" WHERE "events"."establishment_id" = $1 AND ((start_at <= '2020-04-22 16:05:59' AND end_at >= '2020-04-22 16:05:59' AND end_at <= '2020-04-22 18:05:59') OR
           (end_at >= '2020-04-22 18:05:59' AND start_at <= '2020-04-22 18:05:59' AND start_at >= '2020-04-22 16:05:59') OR
            (start_at >= '2020-04-22 16:05:59' AND end_at <= '2020-04-22 18:05:59') OR
           (start_at <= '2020-04-22 16:05:59' AND end_at >= '2020-04-22 18:05:59'))  [["establishment_id", 1]]
  Event Create (17.1ms)  INSERT INTO "events" ("uuid", "establishment_id", "name", "start_at", "end_at", "max_female_guest", "max_male_guest", "created_at", "updated_at", "published") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING "id"  [["uuid", "f110cba0-1f97-45ed-bc84-a8bebc73b8f8"], ["establishment_id", 1], ["name", "Clifford Kulas Conroy"], ["start_at", "2020-04-22 16:05:59"], ["end_at", "2020-04-22 18:05:59"], ["max_female_guest", 5], ["max_male_guest", 5], ["created_at", "2020-04-20 07:49:21.830331"], ["updated_at", "2020-04-20 07:49:21.830331"], ["published", true]]
  Establishment Update All (0.6ms)  UPDATE "establishments" SET "events_count" = COALESCE("events_count", 0) + $1 WHERE "establishments"."id" = $2  [["events_count", 1], ["id", 1]]
   (0.2ms)  RELEASE SAVEPOINT active_record_1
   (0.4ms)  SAVEPOINT active_record_1
  Area Create (8.1ms)  INSERT INTO "areas" ("uuid", "name", "slug", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["uuid", "e13cec8d-4767-490f-a909-f07970006743"], ["name", "Derick Robel"], ["slug", "derick-robel"], ["created_at", "2020-04-20 07:49:21.970034"], ["updated_at", "2020-04-20 07:49:21.970034"]]
   (0.3ms)  RELEASE SAVEPOINT active_record_1
   (0.3ms)  SAVEPOINT active_record_1
  User Exists? (0.9ms)  SELECT 1 AS one FROM "users" WHERE "users"."email" = $1 LIMIT $2  [["email", "vernon@bergstrom.biz"], ["LIMIT", 1]]
  User Create (18.8ms)  INSERT INTO "users" ("uuid", "email", "encrypted_password", "confirmation_token", "confirmed_at", "confirmation_sent_at", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING "id"  [["uuid", "3bb5012a-ff5b-4dc8-b474-98aa06298c5c"], ["email", "vernon@bergstrom.biz"], ["encrypted_password", "$2a$04$dnmdv.wKESDVpFTn9UyqE.uFQwCexFGcmYBdg18HfGrvv0/4xrrQm"], ["confirmation_token", "bfa804e9-cd89-49ba-acb5-3afcfd5425f6"], ["confirmed_at", "2020-04-20 07:49:21.916321"], ["confirmation_sent_at", "2020-04-20 06:49:21.916422"], ["created_at", "2020-04-20 07:49:22.008360"], ["updated_at", "2020-04-20 07:49:22.008360"]]
  AreasUser Create (4.8ms)  INSERT INTO "areas_users" ("area_id", "user_id") VALUES ($1, $2)  [["area_id", 1], ["user_id", 1]]
   (0.2ms)  RELEASE SAVEPOINT active_record_1
   (0.2ms)  SAVEPOINT active_record_1
  Consumer Create (8.4ms)  INSERT INTO "consumers" ("user_id", "first_name", "last_name", "whatsapp_user_name", "phone_number", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id"  [["user_id", 1], ["first_name", "Kermit"], ["last_name", "Cremin"], ["whatsapp_user_name", "7a206657-8365-4075-b4e8-b58d421ac2e2"], ["phone_number", "+855 618.798.2641"], ["created_at", "2020-04-20 07:49:22.036784"], ["updated_at", "2020-04-20 07:49:22.036784"]]
   (0.3ms)  RELEASE SAVEPOINT active_record_1
   (0.2ms)  SAVEPOINT active_record_1
  Guest Create (18.1ms)  INSERT INTO "guests" ("uuid", "gender", "birthdate", "profession", "insta_user_name", "consumer_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING "id"  [["uuid", "47d43976-e9a9-49d4-a722-22c62821d3b4"], ["gender", 1], ["birthdate", "1998-09-11 00:00:00"], ["profession", "Global Sales Engineer"], ["insta_user_name", "jeannetta.harber"], ["consumer_id", 1], ["created_at", "2020-04-20 07:49:22.049803"], ["updated_at", "2020-04-20 07:49:22.049803"]]
   (0.2ms)  RELEASE SAVEPOINT active_record_1
   (0.2ms)  SAVEPOINT active_record_1
  Invitation Create (12.8ms)  INSERT INTO "invitations" ("guest_id", "event_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["guest_id", 1], ["event_id", 1], ["created_at", "2020-04-20 07:49:22.071668"], ["updated_at", "2020-04-20 07:49:22.071668"]]
  Event Update All (0.5ms)  UPDATE "events" SET "invitations_count" = COALESCE("invitations_count", 0) + $1 WHERE "events"."id" = $2  [["invitations_count", 1], ["id", 1]]
   (0.2ms)  RELEASE SAVEPOINT active_record_1
  Invitation Load (0.5ms)  SELECT "invitations".* FROM "invitations" WHERE "invitations"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  Invitation Load (1.2ms)  SELECT "invitations".* FROM "invitations" WHERE "invitations"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  Invitation Load (0.3ms)  SELECT "invitations".* FROM "invitations" WHERE "invitations"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
   (3.3ms)  SAVEPOINT active_record_1
  Invitation Load (0.9ms)  SELECT "invitations".* FROM "invitations" WHERE "invitations"."id" = $1 LIMIT $2 FOR UPDATE  [["id", 1], ["LIMIT", 1]]
  Guest Load (0.4ms)  SELECT "guests".* FROM "guests" WHERE "guests"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  Event Load (0.4ms)  SELECT "events".* FROM "events" WHERE "events"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  Invitation Update (0.7ms)  UPDATE "invitations" SET "updated_at" = $1, "notification_statuses" = $2 WHERE "invitations"."id" = $3  [["updated_at", "2020-04-20 07:49:22.111406"], ["notification_statuses", "{\"emails\":[{\"created_at\":\"2020-04-20T07:49:22.105Z\"}]}"], ["id", 1]]
   (0.2ms)  RELEASE SAVEPOINT active_record_1
  Invitation Load (0.4ms)  SELECT "invitations".* FROM "invitations" WHERE "invitations"."id" = $1 LIMIT $2 FOR UPDATE  [["id", 1], ["LIMIT", 1]]
  Invitation Load (0.3ms)  SELECT "invitations".* FROM "invitations" WHERE "invitations"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
   (0.2ms)  SAVEPOINT active_record_1
  Invitation Load (1.1ms)  SELECT "invitations".* FROM "invitations" WHERE "invitations"."id" = $1 LIMIT $2 FOR UPDATE  [["id", 1], ["LIMIT", 1]]
  Guest Load (0.9ms)  SELECT "guests".* FROM "guests" WHERE "guests"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  Event Load (0.3ms)  SELECT "events".* FROM "events" WHERE "events"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  Invitation Update (0.7ms)  UPDATE "invitations" SET "updated_at" = $1, "notification_statuses" = $2 WHERE "invitations"."id" = $3  [["updated_at", "2020-04-20 07:49:22.129222"], ["notification_statuses", "{\"emails\":[{\"created_at\":\"2020-04-20T07:49:22.105Z\"},{\"created_at\":\"2020-04-20T07:49:22.124Z\"}]}"], ["id", 1]]
   (0.2ms)  RELEASE SAVEPOINT active_record_1
  Guest Load (0.3ms)  SELECT "guests".* FROM "guests" WHERE "guests"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  Invitation Load (1.1ms)  SELECT "invitations".* FROM "invitations" WHERE "invitations"."id" = $1 LIMIT $2 FOR UPDATE  [["id", 1], ["LIMIT", 1]]
   (2.1ms)  SAVEPOINT active_record_1
  Invitation Update (2.7ms)  UPDATE "invitations" SET "updated_at" = $1, "notification_statuses" = $2 WHERE "invitations"."id" = $3  [["updated_at", "2020-04-20 07:49:22.136369"], ["notification_statuses", "{\"emails\":[{\"created_at\":\"2020-04-20T07:49:22.105Z\"},{\"created_at\":\"2020-04-20T07:49:22.124Z\"},{\"created_at\":\"2020-04-20T07:49:22.135Z\"}]}"], ["id", 1]]
   (0.5ms)  RELEASE SAVEPOINT active_record_1
  Invitation Load (4.1ms)  SELECT "invitations".* FROM "invitations" WHERE "invitations"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
   (1.0ms)  ROLLBACK
   (0.3ms)  BEGIN
   (3.4ms)  ALTER TABLE "tags" DISABLE TRIGGER ALL;ALTER TABLE "schema_migrations" DISABLE TRIGGER ALL;ALTER TABLE "ar_internal_metadata" DISABLE TRIGGER ALL;ALTER TABLE "areas_establishments" DISABLE TRIGGER ALL;ALTER TABLE "consumer_ratings" DISABLE TRIGGER ALL;ALTER TABLE "addresses" DISABLE TRIGGER ALL;ALTER TABLE "oauth_access_tokens" DISABLE TRIGGER ALL;ALTER TABLE "guests" DISABLE TRIGGER ALL;ALTER TABLE "areas_users" DISABLE TRIGGER ALL;ALTER TABLE "active_storage_blobs" DISABLE TRIGGER ALL;ALTER TABLE "areas" DISABLE TRIGGER ALL;ALTER TABLE "invitations" DISABLE TRIGGER ALL;ALTER TABLE "establishments_managers" DISABLE TRIGGER ALL;ALTER TABLE "consumers" DISABLE TRIGGER ALL;ALTER TABLE "events" DISABLE TRIGGER ALL;ALTER TABLE "taggings" DISABLE TRIGGER ALL;ALTER TABLE "users" DISABLE TRIGGER ALL;ALTER TABLE "oauth_applications" DISABLE TRIGGER ALL;ALTER TABLE "establishments" DISABLE TRIGGER ALL;ALTER TABLE "active_storage_attachments" DISABLE TRIGGER ALL;ALTER TABLE "oauth_access_grants" DISABLE TRIGGER ALL;ALTER TABLE "managers" DISABLE TRIGGER ALL;ALTER TABLE "devices" DISABLE TRIGGER ALL;ALTER TABLE "admins" DISABLE TRIGGER ALL
   (5.0ms)  COMMIT
   (2.3ms)            SELECT schemaname || '.' || tablename
          FROM pg_tables
          WHERE
            tablename !~ '_prt_' AND
             tablename <> 'schema_migrations'  AND tablename <> 'ar_internal_metadata'  AND
            schemaname = ANY (current_schemas(false))

   (1.7ms)  select table_name from information_schema.views where table_schema = 'secret_core_test'
   (1425.7ms)  TRUNCATE TABLE "public"."tags", "public"."areas_establishments", "public"."consumer_ratings", "public"."addresses", "public"."oauth_access_tokens", "public"."guests", "public"."areas_users", "public"."active_storage_blobs", "public"."areas", "public"."invitations", "public"."establishments_managers", "public"."consumers", "public"."events", "public"."taggings", "public"."users", "public"."oauth_applications", "public"."establishments", "public"."active_storage_attachments", "public"."oauth_access_grants", "public"."managers", "public"."devices", "public"."admins" RESTART IDENTITY CASCADE;
   (0.5ms)  BEGIN
   (2.4ms)  ALTER TABLE "tags" ENABLE TRIGGER ALL;ALTER TABLE "schema_migrations" ENABLE TRIGGER ALL;ALTER TABLE "ar_internal_metadata" ENABLE TRIGGER ALL;ALTER TABLE "areas_establishments" ENABLE TRIGGER ALL;ALTER TABLE "consumer_ratings" ENABLE TRIGGER ALL;ALTER TABLE "addresses" ENABLE TRIGGER ALL;ALTER TABLE "oauth_access_tokens" ENABLE TRIGGER ALL;ALTER TABLE "guests" ENABLE TRIGGER ALL;ALTER TABLE "areas_users" ENABLE TRIGGER ALL;ALTER TABLE "active_storage_blobs" ENABLE TRIGGER ALL;ALTER TABLE "areas" ENABLE TRIGGER ALL;ALTER TABLE "invitations" ENABLE TRIGGER ALL;ALTER TABLE "establishments_managers" ENABLE TRIGGER ALL;ALTER TABLE "consumers" ENABLE TRIGGER ALL;ALTER TABLE "events" ENABLE TRIGGER ALL;ALTER TABLE "taggings" ENABLE TRIGGER ALL;ALTER TABLE "users" ENABLE TRIGGER ALL;ALTER TABLE "oauth_applications" ENABLE TRIGGER ALL;ALTER TABLE "establishments" ENABLE TRIGGER ALL;ALTER TABLE "active_storage_attachments" ENABLE TRIGGER ALL;ALTER TABLE "oauth_access_grants" ENABLE TRIGGER ALL;ALTER TABLE "managers" ENABLE TRIGGER ALL;ALTER TABLE "devices" ENABLE TRIGGER ALL;ALTER TABLE "admins" ENABLE TRIGGER ALL
   (1.8ms)  COMMIT
...