Prazni ekrani posle duzeg stajanja — nije GC (dokazano)

Pukla veza + dva tiha fallback-a; dokazano sa KILL; i tri sloja tisine u samom logu

↑ Nazad

Simptom, prijavljivan godinama: ako aplikacija ostane uključena duže od oko 24 sata, posle toga svi ekrani vraćaju prazno — do restarta. Radna hipoteza je bila da garbage collector počisti nešto što ne sme.

Nije GC. Uzrok je pucanje veze sa bazom, plus dva tiha fallback-a koja ga pretvore u tišinu umesto u poruku o grešci.

## ✅ Dokazano eksperimentom Veza aplikacije je ubijena iz GUI klijenta (KILL <id>), i simptom se reprodukovao odmah — prazni ekrani, bez ijedne poruke o grešci. Nije više zaključak iz čitanja koda nego reprodukovan kvar, sa uzrokom i poznatim okidačem.

Mehanizam, ceo iz izvora

korakdokaz
Veza se otvara jednomDriverManager.getConnection("jdbc:mysql://" + host + "/" + baza, ...) — GMySqlDataLink.java:285, bez autoReconnect
Statement se pravi jednom, odmah posleopenStatement() :63 → createStatement(TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE) :149
Ništa nikad ne proverava da li je veza živau celom fajlu nema isClosed, isValid, ni ponovnog povezivanja
Server sam zatvara neaktivnu vezuwait_timeout nije zadat u my.ini → MySQL podrazumevano 28800 s = 8 h
Greška se gutaexecuteQuery na SQLException loguje i vrati se normalno (:130-135)

Posledica: prvi upit posle isteka baci izuzetak, vrati se prazan rezultat, uvoz podataka ne doda ni jedan red → prazan ekran. Ništa se ne povezuje ponovo, pa ostaje pokvareno do restarta.

„Duže od 24 sata" se uklapa — sve iznad 8 sati neaktivnosti.

Zašto se greška nikad nije videla — tri nezavisna sloja tišine

Ovo je vredelo raspetljati, jer je razlog zašto je hipoteza o GC-u bila razumna: nije bilo nikakvog signala. Kad je logovanje uključeno da uhvati izuzetak, LogFile.txt je ostao 0 bajtova. Traženje uzroka pokazalo je da postoje tri zasebna prekidača, i svaki sam za sebe dovoljan da sve ućuti.

1. Uređaj za logovanje je podrazumevano isključen

Podrazumevana vrednost LoggingDevice je „bez logovanja". Ispravna vrednost za fajl je F + tačno dva razmaka (polje je fiksne širine 3).

2. Svi tipovi događaja su zakucani na false

Uslovni ispis propušta samo ako je tip događaja „logabilan". U enumu događaja jedini koji je true je ALWAYS — a on se ne koristi nigde. Tip za SQL (DatabaseMySql), kao i Database i Print, svi su false. Za to ne postoji konfiguracioni prekidač — to su konstante u kodu.

Glavni prekidač „loguj sve" postoji kao promenljiva, ali je deklarisan false i nigde se ne postavlja. Mrtav kod.

Praktična podela: 25 poziva je bezuslovno, 39 je filtrirano. Srećom, ono što je potrebno za dijagnostiku je u bezuslovnim:

štaprolazi bez izmene koda?
greške baze (SQLException + tekst upita)da
merenja SortTime: i Diff+:da (uz testSpeed = true)
svaki SQL upitne — traži DatabaseMySql → true u izvoru + rebuild

3. Bafer se ne prazni — log ne preživi pad

Ovo je najvažniji deo, i najopasniji:

Posledica: sadržaj stoji u baferu i vidi se tek posle urednog izlaska iz aplikacije. Ako aplikacija padne, bude ubijena ili zatvorena preko Task Manager-a — log je prazan.

Dakle dijagnostički kanal ne preživi upravo onaj kvar zbog kojeg bi se čitao. To objašnjava i zašto je raniji „tihi pad" bio tako težak za dijagnozu.

Kako ga koristiti danas

Izađi iz aplikacije kroz meni ili zatvaranje prozora, ne ubijanjem procesa — pa čitaj LogFile.txt. I prepiši ga u stranu pre sledećeg pokretanja, jer start briše.

Popravke logovanja — male, lokalne, nezavisne od svega ostalog

  1. otvoriti fajl sa StandardOpenOption.CREATE + StandardOpenOption.APPEND
  2. flush() posle svakog reda (ili PrintWriter sa autoflush) — sadržaj vidljiv dok program radi
  3. opciono: ime fajla sa datumom, da se sesije ne gaze

Dva tiha fallback-a u nizu

  1. GDbTable: ako dobavljanje aktivne baze baci izuzetak → tip pada na MEMORY. Tabela tiho postane memorijska, bez podataka i bez greške.
  2. GMySqlDataLink.executeQuery: if(mStatement == null) return l; → prazan rezultat; a SQLException se loguje i ne propagira.

Zato je „ekran se otvori a nema ništa" prvo mesto gde treba proveriti vezu, a ne podatke.


Dokaz u 30 sekundi, bez čekanja 8 sati

Iz GUI klijenta za bazu, poveži se kao root (za viđenje i ubijanje tuđih sesija treba privilegija PROCESS/SUPER):

-- 1. sve otvorene sesije; TTIME uz COMMAND='Sleep' je brojac do wait_timeout-a
SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE, LEFT(INFO, 100) AS POSLEDNJI_UPIT
FROM information_schema.PROCESSLIST
WHERE ID <> CONNECTION_ID()        -- ne prikazuj moju sopstvenu sesiju
ORDER BY TIME DESC;

-- 2. ubij vezu aplikacije (DB = ime baze aplikacije)
KILL 123;

KILL je podrazumevano KILL CONNECTION — zatvori celu vezu, tačno kao server po wait_timeout-u. (KILL QUERY bi prekinuo samo tekući upit i ne bi reprodukovao kvar.)

Pa klikni ekran u aplikaciji. Rezultat: prazan grid, bez ijedne poruke o grešci. Potvrđeno.

Verniji test — pravi okidač, ne simulacija

SET GLOBAL wait_timeout = 30;      -- vazi samo za NOVE veze

Pa restartuj aplikaciju, pusti je da stoji 40 sekundi bez klika, i klikni ekran. Bezbedno: ne upisuje se u my.ini i vraća se na podrazumevano pri restartu servisa. Posle: SET GLOBAL wait_timeout = 28800;


Popravke, od najjeftinije

  1. Popraviti sam log (APPEND + flush) — bez toga se sve ostalo popravlja naslepo. Najmanja izmena sa najvećim uticajem na svaku buduću dijagnostiku.
  2. LoggingDevice=F u gengine.cfg. Ne rešava kvar, ali greške baze od tada prolaze (bezuslovni ispis). Za svaki SQL dodatno treba DatabaseMySql → true u izvoru + rebuild.
  3. autoReconnect=true u JDBC URL. Jedna reč. Stari Connector/J to podržava; ograničenje je da ne vraća stanje sesije, pa je to ublažavanje a ne rešenje.
  4. Prava popravka, na jednom šavu: u executeQuery, na SQLException tipa „veza", ponovo otvoriti vezu i Statement i probati jednom; a stvarni neuspeh prijaviti, ne vratiti prazan rezultat. Pošto sav SQL prolazi kroz to jedno mesto, popravka je lokalna.
  5. Povećanje wait_timeout-a na serveru samo odlaže — pucanje mreže se i dalje dešava.

Lekcija koja se prenosi na druge sisteme

Tiha rezerva je gora od greške. Oba fallback-a su napisana da program „ne padne" — a rezultat je da je godinama davao pogrešan odgovor (prazno) umesto da kaže šta ne radi. Cena nije bila pad nego izgubljeno vreme na pogrešnu hipotezu: sumnjalo se na GC, jer je to bilo jedino što je ostalo kad nema nikakve poruke.

Uz to, isti kod ima ispravan kanal za prijavu (GLog) — samo je isključen podrazumevanom konfiguracijom. Uključivanje logovanja je najjeftiniji dijagnostički potez u celom sistemu.

Izvor: 13-prazni-ekrani-posle-24h.md