Razno Vodič

Migracija sa SQLite na PostgreSQL

sqlite

SQLite može da bude sasvim dovoljan dok je vaša aplikacija u fazi razvoja i još uvek mala. U toj fazi je uglavnom jedna instanca, mali broj korisnika, a upisi u bazu nisu toliko česti. Sve to SQLite rešava bez ikakvih problema. Međutim, kada aplikacija uđe u zreliju fazu produkcije i kada počne da dobija više paralelnih zahteva, backend aktivnosti ili više instanci, SQLite počinje da pokazuje ograničenja koja su jednostavno posledica dizajna.

Ono što najčešće može da bude probleme je konkurentnost upisa. S obzirom da je u pitanju baza koja skladišti podatke u fajlu, zaključavanje pri pisanju može da dovede do situacija gde logički nezavisne operacije počnu da čekaju jedna drugu. 

Na nivou korisničkog iskustva to postaje da se primećuje kao usporenje aplikacije ili duže čekanje na upis podataka. Ovo je siguran znak da SQLite više nije dovoljan za vašu aplikaciju. Srećom tu na scenu stupa PostgreSQL.

U ovom tekstu ćemo objasniti kako da uradite migraciju sa SQLite na PostreSQL, sa posebnim osvrtom na dobru pripremu i planiranje svih koraka migracije. Videćete da je upravo ovo poslednje ključno da biste ovu migraciju uspešno sproveli do kraja.

Radi praktičnost smo ceo proces migracije podelili na nekoliko faza, pa predlažemo da detaljno i dosledno pratite navedene korake.

Faza 1: Odredite strategiju i rizike

Pre nego što unesete bilo koju komandu ili počnete da razmišljate o alatima i skriptama, neophodno je da sebi odgovorite na jedno ključno pitanje: da li vaša aplikacija može da toleriše downtime tokom migracije baze podataka?

Na prvi pogled, ovo pitanje deluje trivijalno, ali u praksi upravo ono određuje ceo dalji tok migracije. Odgovor na ovo pitanje utiče na arhitekturu rešenja, složenost procesa, rizike koje preuzimate i vreme koje ćete kasnije potrošiti na testiranje i validaciju.

Ako vaša aplikacija sme da ima privremeni prekid rada, migracija se može sprovesti na jednostavan, predvidiv i kontrolisan način. U tom slučaju proces izgleda jasno i linearno: prvo se zaustavljaju svi upisi u bazu, zatim se postojeći podaci prebacuju u novu bazu, nakon toga se proverava integritet i ispravnost podataka, i tek na kraju se aplikacija prebacuje da koristi novu bazu. Ovakav pristup je lak za razumevanje, lak za debugovanje i značajno smanjuje mogućnost grešaka.

Situacija se značajno komplikuje ako downtime nije dozvoljen. Tada ulazite u naprednije obrasce migracije, poput takozvanog dual-write pristupa, gde aplikacija u određenom periodu paralelno upisuje podatke u staru i novu bazu. Ideja je da se tranzicija obavi postepeno, bez prekida rada aplikacije, ali to sa sobom nosi dodatne tehničke izazove. Potrebno je sinhronizovati podatke, voditi računa o konzistentnosti i rešavati potencijalne konflikte između dva sistema.

Postoji i jedan praktičan paradoks koji se često zanemaruje, a to je da dual-write povećava broj upisa. Ako ste sa SQLite-a krenuli da migrirate upravo zbog problema sa performansama ili zaključavanjem baze, paralelno pisanje u dve baze može dodatno pogoršati situaciju i stvoriti nove probleme koje ranije niste imali.

Zbog toga se u realnim projektima, naročito kod prve migracije i kod većine manjih i srednjih sistema, maintenance prozor pokazuje kao najrazumnije rešenje. On vam daje čist i jasan presek stanja sistema: u tačno definisanom trenutku znate da niko ne upisuje podatke, da se baza ne menja i da migracija ima jasan početak i kraj.

To ne samo da smanjuje rizik tokom samog prebacivanja, već i značajno olakšava kasniju validaciju, jer možete sa sigurnošću da proverite da li su svi podaci ispravno preneti.

Faza 2: Identifikujte podatke u SQLite bazi

Ovo je faza koju mnogi preskoče jer deluje kao nepotrebno zadržavanje, ali u praksi upravo ona najviše smanjuje rizik od problema tokom migracije.

SQLite je veoma tolerantna baza podataka. Zahvaljujući sistemu afiniteta tipova, deklaracija kolone ne garantuje da su u njoj samo podaci tog tipa. To u praksi znači da kolona INTEGER može sadrži tekst, TEXT kolona može da ima numeričke vrednosti, i sve to može da funkcioniše bez ikakvih grešaka. Ta fleksibilnost SQLIte-a je prednost tokom razvoja, jer omogućava brz rad bez stroge validacije.

Međutim, prelazak na PostgreSQL podrazumeva potpuno drugačiju logiku. PostgreSQL je striktno tipiziran sistem i ne dozvoljava odstupanja: ako kolona očekuje integer, string jednostavno neće biti prihvaćen. Ono što je u SQLite-u prolazilo neprimećeno, ovde postaje greška koja prekida migraciju ili onemogućava upis podataka.

Zbog toga se u ovoj fazi ne posmatra samo šema baze, već i stvarni sadržaj podataka. Cilj je da se proveri da li podaci zaista odgovaraju planiranim tipovima i da se na vreme otkriju nekonzistentnosti koje bi kasnije napravile ozbiljan problem. Ovaj korak omogućava da migracija bude predvidiva, kontrolisana i bez neprijatnih iznenađenja.

2.1 Prvi pregled: tabele i šema

Prvi korak u ovoj fazi je da pregledate postojeću šemu SQLite baze. Najlakši način za to je kroz SQLite CLI.

Osnovne komande koje ćete koristiti su:

  • .tables – prikazuje sve tabele u bazi
  • .schema – prikazuje kompletnu šemu baze

Ako vas zanima konkretna tabela, možete pogledati samo nju:

  • .schema users

Ovaj korak je važan jer vam pomaže da steknete širu sliku sistema: koje tabele postoje, koje su ključne za aplikaciju, koje su velike i kako su međusobno povezane. Migracija nije samo tehničko prebacivanje podataka, već i razumevanje strukture koju premeštate.

Dok analizirate šemu, obratite pažnju na nekoliko tipičnih obrazaca koji su česti u SQLite bazama i koji zahtevaju odluke prilikom migracije.

Ako uočite kolone definisane kao INTEGER PRIMARY KEY, imajte u vidu da je to u SQLite-u poseban slučaj. Takva kolona je u stvari alias za ROWID i ponaša se kao auto-increment, iako nije identična ponašanju AUTOINCREMENT ključne reči. U PostgreSQL-u se ovo najčešće mapira na SERIAL, BIGSERIAL ili moderniji IDENTITY, u zavisnosti od očekivanog obima podataka.

Ako primetite da su boolean vrednosti modelovane kao INTEGER, to je uobičajena praksa u SQLite-u. PostgreSQL, međutim, ima nativni BOOLEAN tip, što znači da ćete tokom migracije morati da prilagodite i tip kolone i upite koji nad njom rade.

Posebnu pažnju obratite na kolone sa datumima i vremenima. U SQLite-u su one često definisane kao TEXT, što samo po sebi nije problem, ali zahteva odluku prilikom migracije. Ako su vrednosti u doslednom ISO formatu (na primer 2025-12-21T10:30:00Z), konverzija je relativno jednostavna. Ako, međutim, u istoj koloni postoje različiti formati ili nekonzistentni zapisi, moraćete da izdvojite dodatno vreme da biste podatke očistili i pravilno mapirali u odgovarajuće PostgreSQL tipove.

Ova analiza šeme vam omogućava da unapred identifikujete potencijalne probleme i da migraciju planirate svesno, umesto da vas greške iznenade tek tokom samog prebacivanja.

2.2 Provera tipova podataka u stvarnim redovima

SQLite ima ugrađenu funkciju typeof() koja pokazuje kako je vrednost stvarno skladištena u bazi, bez obzira na deklarisani tip kolone. To je izuzetno korisno tokom pripreme za migraciju, jer vam omogućava da vidite stvarno stanje podataka, a ne samo ono što šema sugeriše.

Na primer, kolona age bi logički trebalo da sadrži brojčane vrednosti. Kao prvi korak, možete uzeti mali uzorak podataka kako biste stekli opšti utisak:

SELECT id, age, typeof(age) AS age_type
FROM users
WHERE age IS NOT NULL
LIMIT 20;


Ovaj upit vam daje informaciju kakve se vrednosti zaista nalaze u koloni. Nakon toga sledi važniji korak: identifikacija problematičnih redova, odnosno onih koji ne odgovaraju očekivanom tipu:

SELECT id, age
FROM users
WHERE age IS NOT NULL AND typeof(age) <> 'integer';


Isti princip treba primeniti i na kolone koje predstavljaju iznose, poput cene, balansa ili provizije. Ako je u SQLite-u korišćen tip REAL, moguće je da imate floating vrednosti sa velikim brojem decimala ili čak stringove sa zarezom umesto tačke. PostgreSQL tip NUMERIC je veoma pogodan za rad sa novcem, ali podrazumeva uredne i konzistentne vrednosti, pa je važno da ove probleme otkrijete unapred.

Kod datuma je postupak sličan. Prvo proveravate kako su vrednosti zaista skladištene:

SELECT id, created_at, typeof(created_at) AS t
FROM users
WHERE created_at IS NOT NULL
LIMIT 20;

Ako se ispostavi da su datumi skladišteni kao tekst, sledeće ključno pitanje je da li su svi zapisi u istom formatu. Ukoliko nisu, migracija postaje složenija i možda će biti potrebno prelazno rešenje, na primer da se podaci privremeno prebace kao TEXT, a zatim naknadno, kroz kontrolisane korake, normalizuju u odgovarajuće PostgreSQL tipove.

2.3 Foreign keys: da li su u SQLite-u bili uključeni?

U SQLite-u je moguće da enforcement stranih ključeva uopšte nije uključen. Zato je važno da proverite trenutno stanje sledećom komandom:

PRAGMA foreign_keys;

Ako rezultat bude 0, to samo po sebi ne znači da su podaci pogrešni, ali znači da SQLite nije aktivno garantovao integritet relacija između tabela. Drugim rečima, baza je dozvoljavala upise koji u strožem sistemu ne bi prošli bez greške. PostgreSQL, sa druge strane, na ovome striktno insistira.

Zbog toga je pre migracije neophodno proveriti da li u bazi postoje takozvani „siročići“, odnosno redovi koji referenciraju nepostojeće zapise. Klasičan primer je situacija u kojoj orders.user_id mora da postoji u users.id. To možete proveriti sledećim upitom:

SELECT o.id, o.user_id
FROM orders o
LEFT JOIN users u ON u.id = o.user_id
WHERE u.id IS NULL;


Ako ovaj upit vrati rezultate, to je izuzetno korisna informacija pre same migracije. Ona vam daje priliku da svesno odlučite kako da postupite sa takvim podacima: da li su oni zaista validni, kako su nastali i kakvo ponašanje sistema želite da imate ubuduće. Donošenje ovih odluka unapred značajno smanjuje rizik da migracija na PostgreSQL naiđe na neočekivane blokade ili greške.

2.4 Duplikati

SQLite baze, naročito u prototip ili ranoj fazi razvoja, često nemaju definisana UNIQUE ograničenja. Migracija na PostgreSQL je dobar trenutak da uvedete bar osnovna pravila konzistentnosti podataka. Pre nego što to uradite, neophodno je da proverite da li u postojećim podacima već postoje duplikati.

Na primer, ako planirate da u PostgreSQL-u uvedete UNIQUE ograničenje nad kolonom email, prvo morate da proverite trenutno stanje:

SELECT email, COUNT(*) AS c
FROM users
WHERE email IS NOT NULL
GROUP BY email
HAVING COUNT(*) > 1;


Ako ovaj upit vrati redove, a vi pokušate da uvedete UNIQUE(email) u PostgreSQL-u, migracija neće uspeti dok se problem ne reši. I to je zapravo dobra stvar, jer vas primorava da donesete odluku o tome koji zapis smatrate validnim i kako želite da se sistem ponaša ubuduće. Važno je samo da ove situacije otkrijete unapred, a ne tek u kasnoj fazi migracije.

Kada završite ovu analizu, trebalo bi da imate jasnu listu potencijalnih problema koji vas mogu sačekati tokom migracije: neusklađene tipove podataka, nevalidne datume, prekinute relacije i duplikate. Ne morate sve odmah da ispravite, ali morate da budete svesni njihovog postojanja, jer ćete u narednim fazama migracije upravo na osnovu toga odlučivati kako ćete ih tretirati.

Faza 3: backup, snapshot i probna migracija

Sada kada znate šta imate, prelazite na pripremu sigurnosne mreže. Migracija bez pouzdanog povratka unazad je kao deploy bez rollback-a.

3.1 Napravite konzistentan backup SQLite baze

Najbezbednije je da pravljenje backup-a uradite u trenutku kada aplikacija ne vrši upise u bazu, bilo tokom maintenance prozora ili uz privremeno onemogućavanje upisa. Na taj način obezbeđujete da je stanje baze stabilno i nepromenjeno tokom procesa kopiranja.

Za SQLite postoji ugrađena komanda za pravljenje sigurnosne kopije:

sqlite3 path/to/db.sqlite ".backup 'db_backup.sqlite'"


Možda ćete se zapitati zašto koristiti .backup, a ne jednostavno kopirati fajl pomoću cp. Iako direktno kopiranje često funkcioniše, .backup je SQLite-ov mehanizam koji garantuje konzistentnu kopiju baze, čak i kada postoji rizik od istovremenog pristupa. Kod migracija je upravo konzistentnost ključna, jer želite da prebacite bazu tačno u onakvom stanju u kakvom je bila u trenutku pre migracije.

Ako imate mogućnost, uz sam backup zabeležite i stanje aplikacije u tom trenutku: verziju koda, commit hash, kao i verziju ili stanje migracija u okviru ORM-a. Ove informacije su izuzetno korisne ako kasnije budete analizirali razlike između onoga što je baza sadržala i onoga što je aplikacija u tom trenutku očekivala.

3.2 Probna migracija

Ovo je korak koji se gotovo uvek isplati: pre produkcije obavezno uradite probnu migraciju. Idealno na staging okruženju, a ako njega nemate, makar lokalno. Cilj nije samo da se podaci prebace, već da migracija postane rutinski i ponovljiv proces, a ne jednokratna operacija puna neizvesnosti.

Probna migracija vam pokazuje kako se alati ponašaju u praksi: koliko proces traje, gde dolazi do problema sa tipovima podataka, da li postoje encoding ili format problemi i da li šema u PostgreSQL-u zaista odgovara načinu na koji aplikacija koristi bazu.

Tek kada probna migracija prođe čisto i bez nejasnoća, ima smisla razmišljati o produkcionom prebacivanju. Na taj način u produkciju ne ulazite sa pretpostavkama, već sa proverenim i predvidivim procesom.

Faza 4: definisanje ciljne PostgreSQL šeme

Pre nego što prebacite ijedan podatak, morate tačno da znate kako izgleda ciljna šema baze. Ovo je česta tačka na kojoj ljudi prepuste alatu poput pgloader-a da sam generiše strukturu tabela. Rezultat često bude baza koja tehnički radi, ali je semantički loše postavljena: pogrešni tipovi, previše TEXT kolona i nejasna ili nepostojeća ograničenja. Migracija je, zapravo, idealan trenutak da šemu doterate i učinite je dugoročno održivom.

Na primer, jednostavna SQLite tabela može izgledati ovako:

CREATE TABLE users (
  id INTEGER PRIMARY KEY,
  email TEXT,
  created_at TEXT,
  active INTEGER
);


Ista tabela, smisleno modelovana u PostgreSQL-u, može izgledati ovako:

CREATE TABLE users (
  id BIGSERIAL PRIMARY KEY,
  email VARCHAR(255) NOT NULL,
  created_at TIMESTAMPTZ DEFAULT now(),
  active BOOLEAN DEFAULT true
);


BIGSERIAL se često bira jer ostavlja veliki prostor za rast i predstavlja dobar podrazumevani izbor za sisteme koji se vremenom šire. Tip TIMESTAMPTZ obezbeđuje da vreme ima jasan vremenski kontekst, što je posebno važno u savremenim aplikacijama.

U praksi, upravo od tipova i ograničenja zavisi da li će migracija proći bez problema. Ako, na primer, uvedete NOT NULL ograničenje nad kolonom koja je istorijski sadržala NULL vrednosti, migracija neće proći. To ne znači da je ograničenje pogrešno, već da morate doneti svesnu odluku: da li ćete očistiti postojeće podatke ili uvesti prelazni period pre nego što ograničenje postane obavezno.

Ako koristite ORM, najčistiji pristup je da ciljnu šemu definišete kroz ORM migracije. Na taj način baza i aplikacija ostaju sinhronizovane, a alat poput pgloader-a može da se koristi isključivo za punjenje tabela podacima, umesto za njihovo automatsko kreiranje.

Faza 5: Postgres okruženje

Pre same migracije neophodno je da PostgreSQL instanca radi stabilno i bez iznenađenja. Najosnovnija provera je da možete pouzdano da se povežete na bazu:

psql postgresql://user:password@host:5432/dbname


Ako već na ovom koraku nailazite na probleme, potrebno je da ih rešite odmah. Tokom migracije mreža je često najveći izvor frustracija: firewall pravila, IP allowlist, SSL parametri ili pogrešni kredencijali. Ne želite da tek u produkcionom prozoru otkrijete da pristup bazi nije dozvoljen.

Nakon uspešnog povezivanja, proverite da li imate odgovarajuća prava. Ako planirate da alat poput pgloader-a kreira šemu, nalog mora imati dozvole za kreiranje tabela i drugih objekata. Takođe, proverite da li instanca ima dovoljno prostora za inicijalni import podataka.

Iako managed PostgreSQL servisi često obezbeđuju backup i monitoring, odgovornost za to da je instanca pravilno dimenzionisana i spremna za migraciju i dalje ostaje na vama.

Faza 6: pgloader – kako radi, i zašto je koristan

Sada dolazimo do alata.

pgloader je alat za migracije između baza, posebno popularan za SQLite → PostgreSQL. Njegova vrednost je u tome što radi ono što biste vi ručno radili danima: čita SQLite, mapira tipove, prenosi podatke u batch-evima, kreira indekse, i usklađuje sekvence.

6.1 Kako se instalira pgloader

Instalacija pgloader-a je jednostavna i zavisi od operativnog sistema koji koristite.

Na Ubuntu ili Debian sistemima dovoljno je da uradite:

sudo apt update
sudo apt install pgloader


Na macOS-u, ako koristite Homebrew, instalacija izgleda ovako:

brew install pgloader

Alternativno, pgloader možete pokretati i iz Docker kontejnera. Bez obzira na način instalacije, suština je ista: alat mora imati pristup SQLite fajlu i mora biti u mogućnosti da se poveže na PostgreSQL instancu. Kako ćete to tehnički rešiti zavisi od okruženja, ali su ovi uslovi neophodni da bi migracija mogla da se izvrši.

6.2 Najvažnija praksa: koristite load fajl

Kod ozbiljne migracije cilj vam je ponovljiv i kontrolisan proces, zbog čega se koristi migration.load fajl. On omogućava da istu migraciju bez improvizacije ponovite najpre u staging okruženju, a zatim i u produkciji.

Primer jednog migration.load fajla može izgledati ovako:

LOAD DATABASE
     FROM sqlite:///absolute/path/to/db.sqlite
     INTO postgresql://user:password@host:5432/dbname

WITH include drop,
     create tables,
     create indexes,
     reset sequences

SET work_mem to '256MB',
    maintenance_work_mem to '512MB'

CAST
     type int when (= precision 1) to boolean,
     type text to varchar drop not null;


Ovaj fajl treba posmatrati kao skript migracije. U njemu je jasno definisano odakle dolazite, gde idete, koje objekte pgloader kreira, kako se mapiraju tipovi podataka i kako se podešavaju memorijski parametri radi efikasnijeg importa.

Važno je obratiti pažnju na pojedine opcije. Direktiva include drop je izuzetno korisna tokom iteracija na staging okruženju, jer omogućava brzo ponavljanje migracije. U produkciji, međutim, ona mora biti pod strogom kontrolom, jer ne želite da slučajno obrišete pogrešnu bazu ili šemu.

Opcija reset sequences je ključna za ispravno funkcionisanje auto-increment kolona nakon migracije. Bez nje može doći do situacije da prvi novi INSERT pokuša da upiše ID koji već postoji, što dovodi do grešaka odmah nakon puštanja sistema u rad.

Faza 7: pokretanje migracije

Kada je sve pripremljeno, migraciju pokrećete jednostavnom komandom:

pgloader migration.load


U praksi je veoma korisno da sačuvate kompletan izlaz alata, kako biste kasnije mogli da analizirate šta se tačno desilo. To možete uraditi ovako:

pgloader migration.load | tee pgloader.log


Na ovaj način dobijate i prikaz u terminalu i trajni log fajl za kasniju analizu.

Važno je da unapred postavite realna očekivanja: prva migracija često ne prođe bez problema. To ne znači da je migracija neuspešna, već da su podaci do sada živeli u SQLite okruženju gde su mnoga odstupanja bila dozvoljena, dok PostgreSQL takve stvari striktno odbija. Upravo zato je prva migracija dragocena, jer jasno pokazuje koje podatke i odluke još morate da doterate pre konačnog prebacivanja.

7.1 Šta ako migracija pukne? 

Tokom prve migracije najčešće se javljaju tri grupe problema.

Prva su greške pri konverziji tipova. To su situacije u kojima PostgreSQL očekuje broj, a dobija tekst, gde je datum u lošem ili nekonzistentnom formatu, ili gde imate floating vrednosti tamo gde želite precizan NUMERIC.

Druga grupa su problemi sa ograničenjima. Tu spadaju duplikati koji krše UNIQUE ograničenja i prekinute relacije, odnosno strani ključevi koji referenciraju nepostojeće redove.

Treća grupa su problemi sa enkodingom i nedozvoljenim karakterima. PostgreSQL, na primer, ne prihvata \u0000 (null byte) u tekstualnim kolonama, dok je SQLite u tom pogledu tolerantniji.

Proces rešavanja ovih problema je uvek isti: identifikujete uzrok, očistite podatke ili prilagodite šemu, a zatim ponovite migraciju. Ključno je da ne pokušavate da „zaobiđete“ problem bez razumevanja njegove prirode. Ako danas progurate loše podatke, sutra ćete se suočiti sa neobičnim ponašanjima aplikacije koja će biti znatno teža za dijagnostikovanje i ispravljanje.

Faza 8: validacija posle migracije

Verujemo da nema potrebe da naglašavamo važnost validacije podataka nakon migracije.

8.1 Proverite da su tabele i kolone tu gde očekujete

Prvi korak nakon migracije je da proverite da li su tabele i kolone tamo gde ih očekujete i da li imaju ispravne tipove.

U PostgreSQL-u proverite listu tabela:

SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
ORDER BY table_name;


Kolone za konkretnu tabelu:

SELECT column_name, data_type, is_nullable
FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = 'users'
ORDER BY ordinal_position;


Ovaj korak je važan jer migracija može tehnički proći, a da tipovi nisu ono što ste planirali. Ako aplikacija očekuje BOOLEAN, a dobije INTEGER, imaćete semantičke greške koje se teško dijagnostikuju.

8.2 Uporedite broj redova po tabelama (SQLite vs PostgreSQL)

U PostgreSQL-u:

SELECT 'users' AS t, COUNT(*) FROM users
UNION ALL
SELECT 'orders', COUNT(*) FROM orders
UNION ALL
SELECT 'products', COUNT(*) FROM products;


Isti upit pokrenite u SQLite-u. Brojevi moraju da se poklapaju. Ako se ne poklapaju, ne idete dalje dok ne znate zašto.
Ovaj korak deluje banalan, ali hvata najopasnije probleme: tiho izgubljene redove.

8.3 Proverite referencijalni integritet





SELECT o.id, o.user_id
FROM orders o
LEFT JOIN users u ON u.id = o.user_id
WHERE u.id IS NULL;

Idealno stanje je da upit vrati 0 redova.

8.4 Proverite poslovnu semantiku na nekoliko uzoraka

Brojevi se mogu poklapati, a da su vrednosti pogrešno konvertovane. Zato proverite i smisao podataka.

Primer – zbir finansijske kolone:

SELECT SUM(balance) FROM users;


Uporedite rezultat u SQLite-u i PostgreSQL-u. Razlike često ukazuju na probleme sa konverzijom REAL → NUMERIC ili na problematične vrednosti.

Primer – datumi u poslednjih 10 zapisa:

SELECT id, created_at
FROM users
ORDER BY created_at DESC
LIMIT 10;


Poenta nije format, već da vreme ima smisla i da nije pomereno zbog vremenske zone ili pogrešnog parsiranja.

8.5 Testirajte najkritičnije tokove aplikacije

U staging okruženju povežite aplikaciju na PostgreSQL i testirajte ključne tokove: login, kreiranje naloga, unos podataka, porudžbine, pretragu i izveštaje.

Ako imate API, proverite nekoliko realnih zahteva (npr. Postman kolekciju). Ne morate testirati sve ručno, ali morate testirati ono što je poslovno najkritičnije.

9. Prebacivanje aplikacije

Kada prebacite DATABASE_URL na PostgreSQL, obično se javljaju dve klase problema.

Prva je da je aplikacija nesvesno zavisila od SQLite tolerancije. PostgreSQL će baciti grešku tamo gde je SQLite ćutao. To su dobre greške, jer vam jasno pokazuju gde morate da uvedete validaciju, pravilne tipove i jasno rukovanje NULL vrednostima.

Druga klasa problema su konekcije. SQLite nije server, pa otvaranje i zatvaranje konekcije po zahtevu često nije problem. PostgreSQL jeste server i taj model može biti sporiji zbog mreže i handshake-a. Zato se uvodi connection pooling, bilo kroz ORM konfiguraciju ili kasnije kroz PgBouncer. Ne morate sve odmah, ali morate razumeti da konekcioni model više nije isti.

10. Prelazak na produkciju

U maintenance prozoru redosled je ključan.

Prvo zaustavite aplikaciju ili onemogućite upise kako biste dobili konzistentno stanje.

Zatim napravite finalni SQLite backup:

sqlite3 path/to/db.sqlite ".backup 'db_backup.sqlite'"


Nakon toga pokrenite pgloader ka produkcionom PostgreSQL-u i odmah uradite osnovne validacije: da tabele postoje, da se broj redova poklapa za ključne tabele i da su kritične relacije ispravne.

Tek kada je to provereno, prebacite aplikaciju na PostgreSQL i pokrenite je.

Posle puštanja u rad, pratite logove i osnovne signale: greške, latencije, broj konekcija i sporadične timeout-e. Ako nešto ozbiljno pukne, rollback je jasan: vratite aplikaciju na SQLite i nastavite analizu u staging-u. Upravo zato je backup obavezan.

11. Stabilizacija posle migracije

Kada aplikacija stabilno radi na PostgreSQL-u, nemojte odmah da krenete u agresivno fino podešavanje. Prvo posmatrajte realne upite i ponašanje sistema.

Tipično, u ovoj fazi dodajete indekse tamo gde ih stvarni upiti zahtevaju, proveravate da li su tipovi svuda ispravni i uvodite pooling ako konekcije postanu usko grlo.

Migracija se smatra završenom tek kada aplikacija ima predvidivo ponašanje pod opterećenjem i kada su ključni tokovi stabilni.

Zaključak

Kao što ste videli, migracija sa SQLite-a na PostgreSQL nije tehnički hir niti prerana optimizacija, već prirodan korak u razvoju aplikacije koja je izašla iz faze prototipa i počela realno da raste. SQLite je odličan izbor za početak: jednostavan, pouzdan i izuzetno praktičan dok su opterećenje i broj upisa mali.

Međutim, u trenutku kada aplikacija počne da raste i dobija više korisnika, prelazak na PostgreSQL postaje logičan sledeći korak. Ako se migracija uradi planski i disciplinovano, ona je jedno od najisplativijih infrastrukturnih ulaganja koje možete da napravite. Ne donosi vam samo jaču bazu podataka, već stabilnu osnovu za dalji razvoj, skaliranje i pouzdan rad u produkciji.

U tom smislu, uspešna migracija nije kraj posla, već početak ozbiljnije i stabilnije faze životnog ciklusa aplikacije.

Ostavi komentar

Vaša adresa neće biti objavljena