Многие бесполезные запросы, сделанные DataMapper - PullRequest
2 голосов
/ 15 января 2012

У меня проблема с DataMapper (я использую его с Sinatra)

У меня очень простое приложение с 3 моделями. Вот код.

class Level
  include DataMapper::Resource
  property :id, Serial
  property :name, String, :required => true, :unique => true, :lazy => true
  property :description, Text, :lazy => true
  timestamps :at
end

class Player
  include DataMapper::Resource
  property :id, Serial
  property :name, String, :required => true, :lazy => true
  timestamps :at
  belongs_to :game
end

class Game
  include DataMapper::Resource
  property :id, Serial
  has n, :players
  belongs_to :level
  belongs_to :current_player, 'Player', :required => false
end

Вот базовый маршрут:

get '/' do
  DataMapper::logger.debug 'Creating level'
  level = Level.create(:name => "One")

  DataMapper::logger.debug 'Creating game'
  game = Game.create(:level => level)

  DataMapper::logger.debug 'Adding players'
  alice = Player.create(:name => 'Alice', :game => game)
  bob = Player.create(:name => 'Bob', :game => game)

  DataMapper::logger.debug 'Setting game current player'
  game.current_player = alice
  game.save
  'ok'
end

Моя проблема в том, что когда я просматриваю файл журнала DataMapper, я обнаружил, что он сделал много бесполезных запросов, и я не понимаю, почему!

Вот вывод журнала:

 ~ Creating level
 ~ (0.000062) SELECT "id" FROM "levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1
 ~ (0.002241) INSERT INTO "levels" ("name", "created_at", "updated_at") VALUES ('One', '2012-01-15T18:15:28+01:00', '2012-01-15T18:15:28+01:00')
 ~ Creating game
 ~ (0.000048) SELECT "id" FROM "levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1
 ~ (0.001747) INSERT INTO "games" ("level_id") VALUES (1)
 ~ Adding players
 ~ (0.000050) SELECT "id" FROM "levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1
 ~ (0.003762) INSERT INTO "players" ("name", "created_at", "updated_at", "game_id") VALUES ('Alice', '2012-01-15T18:15:28+01:00', '2012-01-15T18:15:28+01:00', 1)
 ~ (0.000085) SELECT "id" FROM "levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1
 ~ (0.001820) INSERT INTO "players" ("name", "created_at", "updated_at", "game_id") VALUES ('Bob', '2012-01-15T18:15:28+01:00', '2012-01-15T18:15:28+01:00', 1)
 ~ Setting game current player
 ~ (0.000078) SELECT "id" FROM "levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1
 ~ (0.001826) UPDATE "games" SET "current_player_id" = 1 WHERE "id" = 1

Как вы можете видеть, существует множество запросов для модели уровня. Я действительно не понимаю, почему DataMapper делает это.

Заранее большое спасибо за помощь.

PS: Вы можете подумать, что это не имеет большого значения, но я действительно упростил структуру модели перед публикацией здесь. Фактическая модель более сложна и полна этих бесполезных запросов ..

Вот короткая часть моего реального файла журнала datamapper: Это происходит, когда я сохраняю экземпляр моей игровой модели.

 ~ (0.001640) UPDATE "asd_games" SET "updated_at" = '2012-01-15T17:51:27+01:00', "current_player_id" = 3, "current_action_id" = 3 WHERE "id" = 1
 ~ (0.000079) SELECT "id", "body" FROM "asd_actions" WHERE "id" = 3 ORDER BY "id"
 ~ (0.000083) SELECT "id", "name", "description" FROM "asd_levels" WHERE "id" = 1 ORDER BY "id"
 ~ (0.000057) SELECT "id" FROM "asd_levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1
 ~ (0.000075) SELECT "id" FROM "asd_levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1
 ~ (0.000083) SELECT "id" FROM "asd_levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1
 ~ (0.000082) SELECT "id" FROM "asd_levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1
 ~ (0.000084) SELECT "id" FROM "asd_levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1

Ответы [ 2 ]

0 голосов
/ 19 декабря 2013

Я столкнулся с той же проблемой, где у меня есть пользователь и работа. Работа принадлежит_ пользователю. Отрывки:

class User
  include DataMapper::Resource
  property :id,                 Serial,   writer: :protected, key: true
  property :email,              String,   required: true, length: (5..40),
                                          unique: true, format: :email_address
end

class Job
  include DataMapper::Resource
  property :id, Serial, key: true
  property :progress, Integer
  property :updated_at, DateTime
  belongs_to :user
end

Каждый раз, когда я сохраняю задание, выполняются два запроса:

~ (0.000421) SELECT `id` FROM `users` WHERE `email` = 'mike@example.com' ORDER BY `id` LIMIT 1
~ (0.001589) UPDATE `jobs` SET `progress` = 19, `updated_at` = '2013-12-19 06:32:43' WHERE `id` = 91

Поскольку я уверен, что мое обновление не содержит неверных данных (ничего не испорчено пользовательским вводом), я смог остановить запуск SELECT с помощью bang (!) Версии save

-    save
+    save!

http://datamapper.org/docs/create_and_destroy.html описывает, что на самом деле происходит, когда вы используете метод bang вместо метода non-bang, поэтому вы захотите просмотреть его и посмотреть, безопасен ли он для вашего варианта использования.

0 голосов
/ 16 января 2012

Дополнительные SELECTS создаются для проверки ограничения :unique => true для класса Level.Эта проверка, похоже, выполняется при каждом вызове базы данных.

Один из способов избежать этого - вместо использования create при создании объектов модели, который сразу сохраняет модель в базе данных, используйте newи затем сохраните весь граф объектов одним вызовом save на подходящем объекте, когда все они будут готовы (см. документы о создании и сохранении моделей ):

DataMapper::logger.debug 'Creating level'
level = Level.new(:name => "One")

DataMapper::logger.debug 'Creating game'
game = Game.new(:level => level)

DataMapper::logger.debug 'Adding players'
alice = Player.new(:name => 'Alice', :game => game)
bob = Player.new(:name => 'Bob', :game => game)

DataMapper::logger.debug 'Setting game current player'
game.current_player = alice
game.save

производит вывод:

 ~ Creating level
 ~ Creating game
 ~ Adding players
 ~ Setting game current player
 ~ (0.000074) SELECT "id" FROM "levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1
 ~ (0.001062) INSERT INTO "levels" ("name", "created_at", "updated_at") VALUES ('One', '2012-01-15T20:07:16+00:00', '2012-01-15T20:07:16+00:00')
 ~ (0.001460) INSERT INTO "games" ("level_id") VALUES (1)
 ~ (0.001279) INSERT INTO "players" ("name", "created_at", "updated_at", "game_id") VALUES ('Alice', '2012-01-15T20:07:16+00:00', '2012-01-15T20:07:16+00:00', 1)
 ~ (0.001592) UPDATE "games" SET "current_player_id" = 1 WHERE "id" = 1

Таким образом, модели сохраняются не сразу, а все вместе, а проверка уникальности выполняется только один раз.

Другая возможность - установить :auto_validation => false в свойстве :name.

Это изменение приводит к выводу (с использованием create):

 ~ Creating level
 ~ (0.001162) INSERT INTO "levels" ("name", "created_at", "updated_at") VALUES ('One', '2012-01-15T20:13:51+00:00', '2012-01-15T20:13:51+00:00')
 ~ Creating game
 ~ (0.001958) INSERT INTO "games" ("level_id") VALUES (1)
 ~ Adding players
 ~ (0.001194) INSERT INTO "players" ("name", "created_at", "updated_at", "game_id") VALUES ('Alice', '2012-01-15T20:13:51+00:00', '2012-01-15T20:13:51+00:00', 1)
 ~ (0.001304) INSERT INTO "players" ("name", "created_at", "updated_at", "game_id") VALUES ('Bob', '2012-01-15T20:13:51+00:00', '2012-01-15T20:13:51+00:00', 1)
 ~ Setting game current player
 ~ (0.001369) UPDATE "games" SET "current_player_id" = 1 WHERE "id" = 1

Таким образом, все еще существует несколько обращений к базе данных, но проверка не выполняетсяне делается при каждом вызове (на самом деле это не похоже на то, что он вообще делается, так что это скорее побеждает объект использования :unique => true в первую очередь).

...