PostgreSQL problem z transakcjami w Rails podczas testów

Hej

Ostatnio podczas wykonywania testów w RSpec dzieje się u mnie dziwna rzecz.

Jeśli włączę pojedynczy test poprzez guard’ to się wykona bez błędu.

Natomiast podczas wykonywania WSZYSTKICH testów wali losowo errorami (tj. raz na 5 razy wszystkie testy przejdą).

Dam skrawek kodu (a co mi tam!) żeby zoobrazować problem:

[code=ruby]describe SentenceService do
def random_sentence
Sentence.find(Sentence.first.id + rand(Sentence.count))
end

before(:all) do
40.times { create(:sentence_with_rules) }
end
after(:all) do
Rule.delete_all
Sentence.delete_all
end
…[/code]
Wynik wywołania pojedyńczego testu:

Skrawek wyniku wywołania wszystkich testów:

[quote]1) SentenceService.text_search given valid parameters given authority_name parameter should return proper sentences
Failure/Error: Sentence.find(Sentence.first.id + rand(Sentence.count))
ActiveRecord::RecordNotFound:
Couldn’t find Sentence with id=816[/quote]
Jest napisane że nie ma Sentence o id 816, ale to w rzeczywistości bullshit bo jak wywołam w środku spec’a

Sentence.count

to otrzymam 40.
A z

Sentence.first.id

Wychodzi 807.

Także powinien być przedział z id 807 - 847.
Ale jak widać to nie działa.

Może mi ktoś wytłumaczyć dlaczego się tak dzieje oraz jaki jest sposób obejścia tego problemu?

PS Info o konfiguracji

[quote]Rails 3.2.3
Ruby 1.9.3
psql (PostgreSQL) 9.1.3
RSpec 2.10.0[/quote]
Database Cleaner używałem. Po wyłączeniu tego gema problem nadal występuje.

Transakcje nie zapewnią Ci, że rekordy będą miały kolejne id. Właściwie nigdy nie powinieneś na to liczyć.

Doprawdy? A możesz to podeprzeć jakąś dokumentacją?

Ja bym dał raczej coś takiego. (strzelam, bo nie mam jak sprawdzić)

def random_sentence Sentence.order('random()').first end

Dzięki za odpowiedź.

Jak narazie rozwiązałem to poprzez:

before(:each)

zamiast

before(:all)

oraz

30.times { create(:sentence_with_rules) }

Obecnie przechodzą wszystkie testy.

Jak skończę jeden feature to się jeszcze pobawię. ;p

before(:all) nie jest wykonywane w traksakcji.

Doprawdy? A możesz to podeprzeć jakąś dokumentacją?[/quote]
Sytuacja przy replikacji master-master w MySQL-u. Zakładam, że dla uniknięcia konfliktu kluczy głównych tak to działa na wszystkich bazach.

Doprawdy błędne założenie. MySQL tworzy nowe ID dla kolum auto increment poza transakcją. Jeśli masz chwilowy split brain to już nie masz replikacji - obie bazy radośnie utworzą nowe rekordy z tym samym ID. Jedynym skutecznym rozwiązaniem jest ustawienie auto_increment_increment (najgłupsza nazwa ustawienia na świecie) na 2 oraz auto_increment_offset na parzystą wartość dla jednego serwera i nieparzystą dla drugiego.

O ile wiem to sekwencje w PostgreSQL już nie mają takiego problemu.

Doprawdy błędne założenie. MySQL tworzy nowe ID dla kolum auto increment poza transakcją.[/quote]
Azaliż albowiem w dokumentacji 5.1 InnoDB napisali, że w przypadku transakcji mogą być dziury w numeracji auto_increment. :wink: