asyncapi-server 1.1.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +5 -5
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +39 -2
  4. data/app/controllers/asyncapi/server/v1/jobs_controller.rb +2 -1
  5. data/app/serializers/asyncapi/server/job_serializer.rb +1 -1
  6. data/app/workers/asyncapi/server/job_status_notifier_worker.rb +51 -0
  7. data/app/workers/asyncapi/server/job_worker.rb +16 -18
  8. data/db/migrate/20141112034324_create_asyncapi_server_jobs.rb +1 -1
  9. data/db/migrate/20141212064931_add_secret_to_asyncapi_server_job.rb +1 -1
  10. data/db/migrate/20150130062520_add_expired_at_to_asyncapi_server_job.rb +1 -1
  11. data/db/migrate/20150201231018_drop_expired_at_from_asyncapi_server_jobs.rb +1 -1
  12. data/lib/asyncapi/server.rb +3 -0
  13. data/lib/asyncapi/server/rails_ext/controller.rb +3 -1
  14. data/lib/asyncapi/server/rspec.rb +20 -7
  15. data/lib/asyncapi/server/version.rb +1 -1
  16. data/spec/controllers/asyncapi/server/v1/jobs_controller_spec.rb +4 -4
  17. data/spec/dummy/db/development.sqlite3 +0 -0
  18. data/spec/dummy/db/schema.rb +2 -2
  19. data/spec/dummy/db/test.sqlite3 +0 -0
  20. data/spec/dummy/log/development.log +81 -0
  21. data/spec/dummy/log/test.log +2011 -0
  22. data/spec/models/job_spec.rb +1 -1
  23. data/spec/requests/enqueueing_jobs_spec.rb +10 -19
  24. data/spec/serializers/job_serializer_spec.rb +1 -0
  25. data/spec/spec_helper.rb +5 -4
  26. data/spec/workers/job_status_notifier_worker_spec.rb +115 -0
  27. data/spec/workers/job_worker_spec.rb +9 -30
  28. metadata +91 -47
  29. data/spec/dummy/db/migrate/20141212065005_create_asyncapi_server_jobs.asyncapi_server.rb +0 -11
  30. data/spec/dummy/db/migrate/20141212065006_add_secret_to_asyncapi_server_job.asyncapi_server.rb +0 -6
  31. data/spec/dummy/db/migrate/20150130062901_add_expired_at_to_asyncapi_server_job.asyncapi_server.rb +0 -12
  32. data/spec/dummy/db/migrate/20150201231329_drop_expired_at_from_asyncapi_server_jobs.asyncapi_server.rb +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 2cd757bb2567503b5ba9d8ce6e83ca056a199305
4
- data.tar.gz: 57cf7ac80d344addd357356b8949e3fdd3bc4fae
2
+ SHA256:
3
+ metadata.gz: d20ce991b75b2d975140fb6ec3d1a9dffb14116167bbcd072007ad7d3b15b9af
4
+ data.tar.gz: e64c1c4061e6b85b320258d995636a402858f96dfe5b66e59b32541296f798c9
5
5
  SHA512:
6
- metadata.gz: 4e9f48b45f660abbed76f254fd0c93ed330c071c816c38f2a0ff7c761ab0110498837594fee02408bc8d906a128478861dbdde68933bd33512c623242fe265dc
7
- data.tar.gz: c0292eab80325a33914f37997f7d7bf79a175c3090ab5f8c2ae7881e86d3315d8bcfe57b71343ad9115ff583b2b08e6e653ed39e5987f767971f1a0cfc262272
6
+ metadata.gz: 77bfca4c01e794d67678f942d89df08afa0f82f8c2b84ae08806e2e00f3cf3cdd67e02f36166952eddc5d6fbeb279fd36659d1df7f43e126713e020f7dbefe1d
7
+ data.tar.gz: 9ef0745fcf8bcd1922f1bd997c6470b2ae05ab1f072e6a8f73e4b4df3d6f906394a2d9c5b464e016b2589370d2f7b3ab536ef2979f0bba3a4fbc5cf1993ac4ff
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2015 G5
1
+ Copyright 2016 G5
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -54,6 +54,37 @@ If you use `protected_attributes`, in an initializer:
54
54
  Asyncapi::Server::Job.attr_accessible :status, :callback_url, :class_name, :params, :secret
55
55
  ```
56
56
 
57
+ ## Usage without Asyncapi::Client
58
+
59
+ If you want to use this without asyncapi client, you need to prepare two things: the endpoint that asyncapi-server will reply to.
60
+
61
+ Create the job by POSTing the following to CreateSomething above:
62
+
63
+ ```json
64
+ {
65
+ "job": {
66
+ "callback_url": "https://myclient.com/jobs_callback",
67
+ "params": {
68
+ "name": "Something's name",
69
+ "approved": true
70
+ },
71
+ "secret": "A secret unique to this job, so that you know what job the server is referring to"
72
+ }
73
+ }
74
+ ```
75
+
76
+ When the server is done processing, it will post something to your client. Your endpoint must accept the following json as the body:
77
+
78
+ ```
79
+ {
80
+ "job": {
81
+ "status": "success",
82
+ "message": "The output of the Runner class (i.e. `CreateSomething`)",
83
+ "secret": "The secret you had sent earlier (this is how you can be sure it's not someone else updating your endpoint)",
84
+ }
85
+ }
86
+ ```
87
+
57
88
  ### RSpec
58
89
 
59
90
  If you want to create an integration spec for you Asyncapi server endpoint, make sure you require the helper:
@@ -65,14 +96,20 @@ require "asyncapi/server/rspec"
65
96
  When you make a request, instead of `post`, use `asyncapi_post`. Ex:
66
97
 
67
98
  ```ruby
68
- asyncapi_post("/api/v1/long_running_job", name: "Compute")
99
+ asyncapi_post("/api/v1/long_running_job", params: { name: "Compute" })
69
100
  ```
70
101
 
71
102
  This helper calls `post` underneath but builds the request in a way that Asyncapi server understands.
72
103
 
104
+ ## Development
105
+
106
+ - Run `rake db:migrate && rake db:migrate RAILS_ENV=test`
107
+ - Make changes
108
+ - `rspec`
109
+
73
110
  ## License
74
111
 
75
- Copyright (c) 2015 G5
112
+ Copyright (c) 2016 G5
76
113
 
77
114
  MIT License
78
115
 
@@ -2,6 +2,7 @@ module Asyncapi
2
2
  module Server
3
3
  module V1
4
4
  class JobsController < ApplicationController # TODO: Asyncapi::Server.parent_controller
5
+ include Rails::Pagination
5
6
 
6
7
  protect_from_forgery with: :null_session
7
8
  respond_to :json
@@ -24,7 +25,7 @@ module Asyncapi
24
25
  job.destroy
25
26
  respond_with job
26
27
  else
27
- render nothing: true, status: 404
28
+ head :not_found
28
29
  end
29
30
  end
30
31
 
@@ -2,7 +2,7 @@ module Asyncapi
2
2
  module Server
3
3
  class JobSerializer < ActiveModel::Serializer
4
4
 
5
- attributes :id, :url, :secret
5
+ attributes :id, :status, :url, :secret
6
6
 
7
7
  end
8
8
  end
@@ -0,0 +1,51 @@
1
+ module Asyncapi::Server
2
+ class JobStatusNotifierWorker
3
+
4
+ include Sidekiq::Worker
5
+ sidekiq_options retry: false
6
+ MAX_RETRIES = 2
7
+
8
+ def perform(job_id, job_message, retries=0)
9
+ @job = Job.find(job_id)
10
+
11
+ report_job_status(job_message)
12
+
13
+ unless @response.code == 200
14
+ if retries <= MAX_RETRIES
15
+ @jid = JobStatusNotifierWorker.perform_async(job_id, job_message, retries+1)
16
+ else
17
+ raise format_error("Something went wrong while poking #{@job.callback_url}")
18
+ end
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def report_job_status(job_message)
25
+ @response ||= Typhoeus.put(
26
+ @job.callback_url,
27
+ body: {
28
+ job: {
29
+ status: @job.status,
30
+ message: @job_message,
31
+ secret: @job.secret,
32
+ }
33
+ }.to_json,
34
+ headers: {
35
+ "Content-Type" => "application/json",
36
+ Accept: "application/json"
37
+ }
38
+ )
39
+ end
40
+
41
+ def format_error(error)
42
+ [
43
+ error,
44
+ "JobID: #{@job.id}",
45
+ "Next Attempt: #{@jid}",
46
+ "HTTP Status: #{@response.code}",
47
+ "HTTP Response: #{@response.inspect}",
48
+ ].join("\n")
49
+ end
50
+ end
51
+ end
@@ -3,8 +3,9 @@ module Asyncapi::Server
3
3
 
4
4
  include Sidekiq::Worker
5
5
  sidekiq_options retry: false
6
+ MAX_RETRIES = 2
6
7
 
7
- def perform(job_id)
8
+ def perform(job_id, retries=0)
8
9
  job = Job.find(job_id)
9
10
  runner_class = job.class_name.constantize
10
11
 
@@ -15,28 +16,25 @@ module Asyncapi::Server
15
16
  job_message = [e.message, e.backtrace].flatten.join("\n")
16
17
  raise e
17
18
  ensure
18
- job.update_attributes(status: job_status)
19
- report_job_status(job, job_message)
19
+ if job
20
+ job.update_attributes(status: job_status)
21
+ report_job_status(job, job_message)
22
+ else
23
+ # For some reason "ActiveRecord::Base.after_transaction",
24
+ # ":after_commit" and ":after_create" does not prevent
25
+ # the ActiveRecord-Sidekiq race condition. In order to
26
+ # prevent this just retry running JobWorker until it finds
27
+ # the job by job_id.
28
+ if retries <= MAX_RETRIES
29
+ JobWorker.perform_async(job_id, retries+1)
30
+ end
31
+ end
20
32
  end
21
33
 
22
34
  private
23
35
 
24
36
  def report_job_status(job, job_message)
25
- Typhoeus.put(
26
- job.callback_url,
27
- body: {
28
- job: {
29
- status: job.status,
30
- message: job_message,
31
- secret: job.secret,
32
- }
33
- }.to_json,
34
- headers: {
35
- "Content-Type" => "application/json",
36
- Accept: "application/json"
37
- }
38
- )
37
+ JobStatusNotifierWorker.perform_async(job.id, job_message)
39
38
  end
40
-
41
39
  end
42
40
  end
@@ -1,4 +1,4 @@
1
- class CreateAsyncapiServerJobs < ActiveRecord::Migration
1
+ class CreateAsyncapiServerJobs < ActiveRecord::Migration[4.2]
2
2
  def change
3
3
  create_table :asyncapi_server_jobs do |t|
4
4
  t.integer :status
@@ -1,4 +1,4 @@
1
- class AddSecretToAsyncapiServerJob < ActiveRecord::Migration
1
+ class AddSecretToAsyncapiServerJob < ActiveRecord::Migration[4.2]
2
2
  def change
3
3
  add_column :asyncapi_server_jobs, :secret, :string
4
4
  end
@@ -1,4 +1,4 @@
1
- class AddExpiredAtToAsyncapiServerJob < ActiveRecord::Migration
1
+ class AddExpiredAtToAsyncapiServerJob < ActiveRecord::Migration[4.2]
2
2
  def change
3
3
  add_column :asyncapi_server_jobs, :expired_at, :datetime
4
4
 
@@ -1,4 +1,4 @@
1
- class DropExpiredAtFromAsyncapiServerJobs < ActiveRecord::Migration
1
+ class DropExpiredAtFromAsyncapiServerJobs < ActiveRecord::Migration[4.2]
2
2
  def up
3
3
  remove_column :asyncapi_server_jobs, :expired_at
4
4
  end
@@ -1,3 +1,6 @@
1
+ require 'active_model_serializers'
2
+ require 'responders'
3
+ require 'ar_after_transaction'
1
4
  require "asyncapi/server/engine"
2
5
 
3
6
  module Asyncapi
@@ -9,8 +9,10 @@ module Asyncapi
9
9
  def async(method_name, klass)
10
10
  define_method(method_name) do
11
11
  job = Job.create(job_params_with(klass.name))
12
+ ActiveRecord::Base.after_transaction do
13
+ JobWorker.perform_async(job.id)
14
+ end
12
15
  serializer = JobSerializer.new(job)
13
- JobWorker.perform_async(job.id)
14
16
  render json: serializer
15
17
  end
16
18
  end
@@ -3,15 +3,28 @@ module Asyncapi
3
3
  module RSpec
4
4
 
5
5
  def asyncapi_post(url, params)
6
- post(url, {
7
- job: {
8
- callback_url: "callback_url",
9
- params: params,
10
- secret: "sekret",
11
- }
12
- })
6
+ formatted_params = format_params(params)
7
+ post(url, formatted_params)
13
8
  end
14
9
 
10
+ private
11
+
12
+ def format_params(params)
13
+ if params.is_a?(Hash) && params.has_key?(:params)
14
+ params = params[:params]
15
+ return { params: base_params(params) }
16
+ else
17
+ return base_params(params)
18
+ end
19
+ end
20
+
21
+ def base_params(params)
22
+ return { job: {
23
+ callback_url: "callback_url",
24
+ params: params,
25
+ secret: "sekret",
26
+ }}
27
+ end
15
28
  end
16
29
  end
17
30
  end
@@ -1,5 +1,5 @@
1
1
  module Asyncapi
2
2
  module Server
3
- VERSION = "1.1.0"
3
+ VERSION = "1.3.0"
4
4
  end
5
5
  end
@@ -10,7 +10,7 @@ module Asyncapi
10
10
  it "returns all jobs" do
11
11
  job_1 = create(:asyncapi_server_job)
12
12
  job_2 = create(:asyncapi_server_job)
13
- get :index, format: :json, page: 2, per_page: 1
13
+ get :index, format: :json, params: { page: 2, per_page: 1 }
14
14
  expect(response).to be_successful
15
15
  parsed_result = indifferent_hash(response.body)
16
16
  expect(parsed_result.first[:id]).to eq job_2.id
@@ -21,7 +21,7 @@ module Asyncapi
21
21
  describe "GET show" do
22
22
  it "returns the job with the given id" do
23
23
  job = create(:asyncapi_server_job)
24
- get :show, format: :json, id: job.id
24
+ get :show, format: :json, params: { id: job.id }
25
25
  expect(response).to be_successful
26
26
  parsed_result = indifferent_hash(response.body)[:job]
27
27
  expect(parsed_result[:id]).to eq job.id
@@ -32,14 +32,14 @@ module Asyncapi
32
32
  describe "DELETE destroy" do
33
33
  it "finds the job by id and secret and deletes it" do
34
34
  job = create(:asyncapi_server_job, secret: "12312")
35
- delete :destroy, format: :json, id: job.id, secret: "12312"
35
+ delete :destroy, format: :json, params: { id: job.id, secret: "12312" }
36
36
  expect(response).to be_successful
37
37
  end
38
38
 
39
39
  context "secret does not match" do
40
40
  it "does not delete the job" do
41
41
  job = create(:asyncapi_server_job, secret: "12312")
42
- delete :destroy, format: :json, id: job.id, secret: "12315"
42
+ delete :destroy, format: :json, params: { id: job.id, secret: "12315" }
43
43
  expect(response.status).to eq 404
44
44
  end
45
45
  end
Binary file
@@ -11,9 +11,9 @@
11
11
  #
12
12
  # It's strongly recommended that you check this file into your version control system.
13
13
 
14
- ActiveRecord::Schema.define(version: 20150201231329) do
14
+ ActiveRecord::Schema.define(version: 20150201231018) do
15
15
 
16
- create_table "asyncapi_server_jobs", force: true do |t|
16
+ create_table "asyncapi_server_jobs", force: :cascade do |t|
17
17
  t.integer "status"
18
18
  t.string "callback_url"
19
19
  t.string "class_name"
Binary file
@@ -0,0 +1,81 @@
1
+  (1.8ms) SELECT sqlite_version(*)
2
+  (1.6ms) SELECT sqlite_version(*)
3
+  (1.3ms) SELECT sqlite_version(*)
4
+  (1.3ms) SELECT sqlite_version(*)
5
+  (1.7ms) SELECT sqlite_version(*)
6
+  (1.7ms) CREATE TABLE "schema_migrations" ("version" varchar NOT NULL PRIMARY KEY)
7
+  (1.2ms) CREATE TABLE "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL)
8
+  (0.2ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
9
+ Migrating to CreateAsyncapiServerJobs (20141112034324)
10
+ TRANSACTION (0.1ms) begin transaction
11
+  (0.6ms) CREATE TABLE "asyncapi_server_jobs" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "status" integer, "callback_url" varchar, "class_name" varchar, "params" text)
12
+ ActiveRecord::SchemaMigration Create (0.2ms) INSERT INTO "schema_migrations" ("version") VALUES (?) [["version", "20141112034324"]]
13
+ TRANSACTION (0.8ms) commit transaction
14
+ Migrating to AddSecretToAsyncapiServerJob (20141212064931)
15
+ TRANSACTION (0.1ms) begin transaction
16
+  (0.6ms) ALTER TABLE "asyncapi_server_jobs" ADD "secret" varchar
17
+ ActiveRecord::SchemaMigration Create (0.2ms) INSERT INTO "schema_migrations" ("version") VALUES (?) [["version", "20141212064931"]]
18
+ TRANSACTION (1.3ms) commit transaction
19
+ Migrating to AddExpiredAtToAsyncapiServerJob (20150130062520)
20
+ TRANSACTION (0.1ms) begin transaction
21
+  (0.6ms) ALTER TABLE "asyncapi_server_jobs" ADD "expired_at" datetime
22
+ Asyncapi::Server::Job Update All (0.2ms) UPDATE "asyncapi_server_jobs" SET "expired_at" = ? WHERE "asyncapi_server_jobs"."expired_at" IS NULL [["expired_at", "2021-03-19 08:45:14.167931"]]
23
+ ActiveRecord::SchemaMigration Create (0.2ms) INSERT INTO "schema_migrations" ("version") VALUES (?) [["version", "20150130062520"]]
24
+ TRANSACTION (0.8ms) commit transaction
25
+ Migrating to DropExpiredAtFromAsyncapiServerJobs (20150201231018)
26
+ TRANSACTION (0.1ms) begin transaction
27
+  (0.1ms) PRAGMA foreign_keys
28
+  (0.1ms) PRAGMA defer_foreign_keys
29
+  (0.1ms) PRAGMA defer_foreign_keys = ON
30
+  (0.1ms) PRAGMA foreign_keys = OFF
31
+  (1.3ms) CREATE TEMPORARY TABLE "aasyncapi_server_jobs" ("id" integer NOT NULL PRIMARY KEY, "status" integer DEFAULT NULL, "callback_url" varchar DEFAULT NULL, "class_name" varchar DEFAULT NULL, "params" text DEFAULT NULL, "secret" varchar DEFAULT NULL, "expired_at" datetime DEFAULT NULL)
32
+  (0.1ms) INSERT INTO "aasyncapi_server_jobs" ("id","status","callback_url","class_name","params","secret","expired_at")
33
+ SELECT "id","status","callback_url","class_name","params","secret","expired_at" FROM "asyncapi_server_jobs"
34
+  (0.5ms) DROP TABLE "asyncapi_server_jobs"
35
+  (0.1ms) CREATE TABLE "asyncapi_server_jobs" ("id" integer NOT NULL PRIMARY KEY, "status" integer DEFAULT NULL, "callback_url" varchar DEFAULT NULL, "class_name" varchar DEFAULT NULL, "params" text DEFAULT NULL, "secret" varchar DEFAULT NULL)
36
+  (0.1ms) INSERT INTO "asyncapi_server_jobs" ("id","status","callback_url","class_name","params","secret")
37
+ SELECT "id","status","callback_url","class_name","params","secret" FROM "aasyncapi_server_jobs"
38
+  (0.1ms) DROP TABLE "aasyncapi_server_jobs"
39
+  (0.0ms) PRAGMA defer_foreign_keys = 0
40
+  (0.0ms) PRAGMA foreign_keys = 1
41
+ ActiveRecord::SchemaMigration Create (0.2ms) INSERT INTO "schema_migrations" ("version") VALUES (?) [["version", "20150201231018"]]
42
+ TRANSACTION (0.7ms) commit transaction
43
+ ActiveRecord::InternalMetadata Load (0.2ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = ? LIMIT ? [["key", "environment"], ["LIMIT", 1]]
44
+ TRANSACTION (0.1ms) begin transaction
45
+ ActiveRecord::InternalMetadata Create (0.4ms) INSERT INTO "ar_internal_metadata" ("key", "value", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["key", "environment"], ["value", "development"], ["created_at", "2021-03-09 08:45:14.199202"], ["updated_at", "2021-03-09 08:45:14.199202"]]
46
+ TRANSACTION (0.7ms) commit transaction
47
+  (0.1ms) SELECT sqlite_version(*)
48
+  (0.2ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
49
+  (1.8ms) SELECT sqlite_version(*)
50
+  (0.4ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
51
+  (0.6ms) SELECT "ar_internal_metadata"."value" FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = ? [["key", "environment"]]
52
+  (0.1ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
53
+  (0.1ms) SELECT "ar_internal_metadata"."value" FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = ? [["key", "environment"]]
54
+  (0.1ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
55
+  (0.1ms) SELECT "ar_internal_metadata"."value" FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = ? [["key", "environment"]]
56
+  (0.1ms) SELECT sqlite_version(*)
57
+  (0.1ms) SELECT sqlite_version(*)
58
+  (0.1ms) DROP TABLE IF EXISTS "asyncapi_server_jobs"
59
+  (1.2ms) CREATE TABLE "asyncapi_server_jobs" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "status" integer, "callback_url" varchar, "class_name" varchar, "params" text, "secret" varchar)
60
+  (1.1ms) CREATE TABLE "schema_migrations" ("version" varchar NOT NULL PRIMARY KEY)
61
+  (0.1ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
62
+  (1.0ms) INSERT INTO "schema_migrations" (version) VALUES (20150201231018)
63
+  (1.2ms) INSERT INTO "schema_migrations" (version) VALUES
64
+ (20141112034324),
65
+ (20141212064931),
66
+ (20150130062520);
67
+
68
+ 
69
+  (1.2ms) CREATE TABLE "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL)
70
+ ActiveRecord::InternalMetadata Load (0.2ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = ? LIMIT ? [["key", "environment"], ["LIMIT", 1]]
71
+ TRANSACTION (0.1ms) begin transaction
72
+ ActiveRecord::InternalMetadata Create (0.4ms) INSERT INTO "ar_internal_metadata" ("key", "value", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["key", "environment"], ["value", "development"], ["created_at", "2021-03-09 08:46:17.185916"], ["updated_at", "2021-03-09 08:46:17.185916"]]
73
+ TRANSACTION (0.7ms) commit transaction
74
+ ActiveRecord::InternalMetadata Load (0.1ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = ? LIMIT ? [["key", "environment"], ["LIMIT", 1]]
75
+ TRANSACTION (0.0ms) begin transaction
76
+ ActiveRecord::InternalMetadata Update (0.3ms) UPDATE "ar_internal_metadata" SET "value" = ?, "updated_at" = ? WHERE "ar_internal_metadata"."key" = ? [["value", "test"], ["updated_at", "2021-03-09 08:46:17.189623"], ["key", "environment"]]
77
+ TRANSACTION (0.7ms) commit transaction
78
+ ActiveRecord::InternalMetadata Load (0.1ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = ? LIMIT ? [["key", "schema_sha1"], ["LIMIT", 1]]
79
+ TRANSACTION (0.0ms) begin transaction
80
+ ActiveRecord::InternalMetadata Create (0.3ms) INSERT INTO "ar_internal_metadata" ("key", "value", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["key", "schema_sha1"], ["value", "b156e6c6d273062fcb56e7a3782b664b02612fc5"], ["created_at", "2021-03-09 08:46:17.192636"], ["updated_at", "2021-03-09 08:46:17.192636"]]
81
+ TRANSACTION (0.6ms) commit transaction