ActiveRecord Migration sekwencja klumny działa jednak nie widać zmian w obiekcie

Witajcie, pracuje obecnie w dosyć mocno rozbudowanym projekcie API pisanym w Railsach.
Case z jakim do Was przybywam jest dosyć specyficzny, zahacza trochę o Railsy, trochę o Postgres’a
Problem z jakim się mierzę związany jest z kolumną w PG, na której działa sekwencja. Z chwilą wykonania query z poziomu aplikacji, dane zapisują się w ROW tabeli, sekwencja na kolumnie działa, autoinkrementacja również, z poziomu POSTICO widać wszystko bardzo ładnie. Co ciekawe, nie widać zmian na tej konkretnej kolumnie z poziomu obiektu, ale może od początku :slight_smile:

MIGRACJA:

add_column :some_tab, :some_col, :bigserial
add_index :some_tab, :some_col, :unique => true
execute “ALTER SEQUENCE some_tab_some_col_seq INCREMENT BY 1 RESTART WITH 2000000000;”

Jak widać poniżej QUERY z poziomu aplikacji bardzo ładnie działa i zapisuje autoincrement’owane wartości do DB

Problem pojawia się w trakcie QUERY, mianowicie w obiekcie, który przenosi dane.
Od początku, kontroler wygląda standardowo jak to w API, na akcji CREATE mamy formularz, przez który user wprowadza jakieś dane. Formularz wraz z innym obiektem przekazywany jest do serwisu. W serwisie mamy trochę logiki, która obraca danymi. I tutaj w tym serwisie dzieje się coś magicznego :slight_smile:

Obiekt, który powinien przenosić w sobie wartość z kolumny some_col zamiast wartości podczas wykonania requestu ma nil’a. Ale wystarczy zrobić na tym obiekcie przeładowanie np: object.reload i już wartość z some_col się pojawia…

Wygląda to tak, jakby na bazie do kolumny some_col wartości nie zapisywały się natychmiast, co jest mocno mało prawdopodobne, ponieważ to właśnie z tej tabeli, z tego ROW, który tworzy się wraz z request’em pobierane są dane do obiektu. A patrząc na bazę, wszystko się w niej odkłada jak należy.

Czy ktoś z Was mierzył się z podobnym problemem ?

Z góry dzięki za info,

Yo,

problem który masz wynika z tego, że AR nie wie jaka wartość została nadana przez bazę danych w Twoim polu. Nic tam nie zapisałeś, nie ma defaulta, AR zakłada, że jest dalej nullem (baza danych nie zwraca zapisanego obiektu). Aż zacząłem sprawdzać skąd AR bierze ID dla nowo zapisanych obiektów, używa rzeczy typu LAST_INSERT_ID w MySQL-u, które zwraca ID ostatnio zapisanego obiektu per połączenie. IMO nie obejdziesz bez reloada (który triggeruje SELECT’a po ID, który ściąga Ci też autoincrementowaną wartość z bazy danych).

Fajne :slight_smile:

1 Like

Witaj Kalaf

Zrobiłem jeszcze jedną migracje, tym razem dałem mu Default’a

> add_column :some_tab, :some_col, :bigint
> add_index :some_tab, :some_col, :unique => true
> execute "CREATE SEQUENCE some_tab_some_col_seq INCREMENT BY 1 START WITH 2000000000;"
> execute "ALTER TABLE some_tab ALTER COLUMN some_col SET DEFAULT nextval('some_tab_some_col_seq');"

Pomimo tego, w dalszym ciągu w obiekcie mam nil’a

Jesteś teraz ActiveRecordem, zrobiłeś inserta do tabeli some_tab. Jaką wartość ma some_col?

Kalaf nie bardzo rozumiem o co chcesz zapytać ?

ActiveRecord zrobił INSERT’a do tabeli some_tab, dane z poziomu POSTICO są widoczne.
Mało tego dane z ROW, do którego AR zrobił INSERT’a również są widoczne w obiekcie, ale nie ma jednego, właśnie some_col, przynajmniej do momentu zrobienia przeładowania.

Co mnie zastanawia, napisałeś że AR dla nowo zapisanych obiektów, używa rzeczy typu LAST_INSERT_ID w MySQL-u, które zwraca ID ostatnio zapisanego obiektu per połączenie. W takim układzie, jeśli w kolumnie mam kilka wpisów dla some_col, dlaczego nie widzi tego ostatniego, i na jego podstawie nie zwraca mi nowego inkrementowanego o wartość 1 w obiekcie ?

Ok, cofnijmy.
INSERT do bazy nie zwraca utworzonego rekordu.
Masz obiekt, któremu nie przypisujesz wartości dla some_coś, więc do bazy leci insert bez tej wartości. Twoja baza akceptuje inserta, generuje sobie wartość dla some_col i ja zapisuje, nie zwracając jej z powrotem do AR.
Dlaczego AR nie sprawdza sobie wszystkich wpisów i nie zgaduje wartości? Bo:

  • Nie byłoby to threadsafe
  • Twoja aplikacja nie musi być jedynym użytkownikiem bazy danych.

MOŻESZ, ale prawdopodobnie nie powinieneś, rozwiązywać tego w aplikacji synchronizując moment zapisu do tej tabeli i wyliczajac wartość w railsach.

Wrzuć wpis do tej tabeli ręcznie, bez railsow i zastanów się skąd będziesz wiedział co pojawiło się w some_col jako klient bazy danych? Powie Ci to wyłącznie select na ostatnim rekordzie (ARowe reload).

Sent from Android cos tam coś tam xD

1 Like

Wszystko jasne, bardzo Ci dziękuje za wyjaśnienie.

Będę musiał robić reload na obiekcie, ale to będzie dodatkowe zapytanie do bazy, to nie jest dobre rozwiązanie.

Kalaf a jak wygląda zapis id ? Każda tabela posiada kolumnę id, każde id jest autoincrementowane, czy nie dzieje się to na podobnej zasadzie ? To ciekawe, bo dla kolumn id wszystko jest zwracane ?

Jeśli chodzi o id to zadałem sobie to samo pytanie po Twoim pierwszym poście i w mojej pierwszej odpowiedzi masz na to odpowiedź :slight_smile: Prawdopodobnie zależy to od bazy danych, w MySQL jest funkcja LAST_INSERT_ID() której używa AR. Zwraca autoinkrementowaną wartość ostatniego rekordu utworzonego przez dane połączenie.

Bardzo ci dziękuje za pomoc w temacie.