Как да направите ActiveRecord ThreadSafe

Как мога да направя следния контролер безопасен за нишки в rails 4 с postgresql:

def controller_action
  if Model.exists(column_name:"some_value")
  else
    @model=Model.new(column_name:"some_value")
    @model.save
  end
end

Работя с puma, така че притеснението ми е, че ако две нишки изпълняват този контролер едновременно и не съществува ред с посочената стойност на column_name, ще бъдат създадени два записа, докато аз искам само 1.


person kempchee    schedule 05.03.2014    source източник
comment
Postgres ще заключи транзакциите, така че първата транзакция ще блокира втората транзакция на db слоя. Традиционно това е начинът, по който се управлява това състезателно състояние.   -  person TheIrishGuy    schedule 06.03.2014
comment
Проблемът ви всъщност няма нищо общо с нишките, имате състояние на състезание на мелницата. Решението е да добавите ограничение в базата данни и да се справите с неизбежните изключения (т.е. да поставите логиката в базата данни, където й е мястото).   -  person mu is too short    schedule 06.03.2014
comment
Така че нека се уверя, че разбирам това... една нишка печели условието за състезание и първо изпълнява Model.exists; междувременно другата нишка е заключена. След @model.save postgresql премахва заключването и другата нишка вижда, че вече съществува ред с желаната стойност. Ако така работи, как postgresql знае кога да премахне заключването? Винаги ли изчаква цялата логика в контролера да приключи, преди да вдигне заключването?   -  person kempchee    schedule 06.03.2014
comment
@TheIrishGuy Глупости. Едновременните актуализации подлежат на заключване. Едновременните вмъквания не се блокират взаимно. Вижте stackoverflow.com/questions/17267417/   -  person Craig Ringer    schedule 06.03.2014


Отговори (2)


Противно на коментарите, едновременните вмъквания в една и съща таблица са напълно допустими в PostgreSQL, така че тук има условие за състезание.

За да направите това безопасно, трябва да имате unique ограничение (или primary key) на column_name. След това дублираните вмъквания ще хвърлят изключение, което можете да хванете и да опитате отново с актуализация.

Ако нямате уникално ограничение, тогава трябва да LOCK TABLE ... IN EXCLUSIVE MODE, за да предотвратите едновременни връщания. Или използвайте един от безопасните за едновременност методи, описани в:

Как да UPSERT (ОБЛИВАНЕ, INSERT ... ON DUPLICATE UPDATE) в PostgreSQL?

person Craig Ringer    schedule 06.03.2014

Да приемем, че нишките имат връзка 1:1 с връзките. Така че, ако имате 100 нишки, които трябва да осъществят достъп до базата данни, трябва да увеличите пула на връзките до 100. Ако имате повече работни нишки, отколкото връзки, трябва да използвате опашка (или друга структура на данни, безопасна за нишки), за да управлявате комуникацията между тях.

person TheIrishGuy    schedule 06.03.2014