Dzień dobry, od jakiegoś czasu próbuję poradzić sobie z następującym problemem.

W jednej chwili działa kilka instancji tego samego skryptu, który wysyła rekordy do innej aplikacji przez jej API. Jeden rekord może być wysłany tylko 1 raz, więc zrobiłem to tak:
  1. SELECT * FROM tabela1 ... WHERE locked = false LIMIT 1 FOR UPDATE

W tym momencie wszystkie zapytania do tabela1 czekają, aż będzie wykonany update na zablokowanym rekordzie
W tym miejscu wykonuję UPDATE:
  1. UPDATE tabela1 SET locked = true WHERE id = :id

Teraz wszystkie zapytania z innych instancji skryptu, które mają pobrać wiersze z tej tabeli mogą dalej pracować i nie pobiorą już zablokowanego wiersza, tylko następny. W ten sposób pozbyłem się wysyłania tego samego rekordu kilka razy. Oczywiście SELECT i UPDATE są wykonywane w transakcji, inaczej się nie da dla FOR UPDATE.

Niestety takie rozwiązanie ma 2 wady:
1. Jest wolne, ponieważ zapytania do tabela1 czekają na zwolnienie blokady wiersza
2. Jeżeli podczas daleszego przetwarzania rekordy coś się wywali, to rekord jest zablokowany na zawsze i już nie zostanie wysłany.

Najlepszym rozwiązaniem dla mnie było by sprawienie, aby pobrany w danej transakcji rekord nie był po prostu widoczny dla innych transakcji do czasu, aż obsługiwana transakcja się zakończy, przy czym nie blokował innych transakcji.

Bardzo proszę o podsunięcie jakiegoś lepszego rozwiązania.