O rozkoszach bugfixów w kodzie Oracle Database. Jako bonus w komentarzach m.in. o podobnych przyjemnościach w kodzie maszyn litograficznych od ASML. https://news.ycombinator.com/item?id=18442941
@slsy: A mi się wydaje że jeśli masz projekt bez zależności w C to pewnie pracują w nim ludzie odporni na pewne "innowacje" - jak precompiled headers albo unity builds. "Innowacje" mam w cudzysłowie bo to techniki obecne od wieeeeelu lat a niektórzy wciąż się przed tym bronią nie wiedzieć czemu, bo przynoszą namacalne rezultaty przy małym wkładzie pracy. Takie prekompilowane nagłówki to ficzer samego kompilatora i nie potrzeba do tego nawet jakiegoś wymślnego systemu budowania. A jeśli include z biblioteki standardowej C++ mocno podbija Ci czas kompilacji to masz problem, który ignorujesz i zwalasz na język z lenistwa. Ile to już będzie lat od kiedy MS zaczął zachęcać do stosowania stdafx.h
? Ponad dwadzieścia?
Z cyklu #AbsurdyKorpo
Dawno nie było ale dzisiaj mi mózg rozpi..ło więc się z wami tym podzielę.
Firma, w której pracuję prowadzi równolegle wiele projektów, a co za tym idzie pozatrudniali mnóstwo nowych osób. Jako, że pracuję tam już sporo czasu to poproszono mnie o udział w projekcie z dwoma nowymi developerami aby przez jakiś czas ich nadzorować. Sęk w tym, że projekt bardzo duży ilość wiedzy dla kogoś nowego ogromna zatem część ustaleń (np architektonicznych) omówiłem bezpośrednio z PM'em oraz architektem zanim tych nowych ludzi pozatrudniali.
Abyście lepiej zrozumieli poziom absurdu opowiem wam też nieco o procesie wydawniczym, który z takich czy innych przyczyn nie jest dobry ale jest to pewien kompromis między potrzebami biznesu i git flow, który chcielibyśmy mieć. Mianowicie dev robią zmiany na środowisku DEV, jak są gotowi idą z tą zmianą na środowisko TEST1. Tam znęca się nad tym zespół QA. Następnie jak QA klepnie idziemy na TEST2 i tam znęca się nad tym biznes i dopiero jak biznes klepnie, że jest ok to idziemy na prod. Każde środowisko TEST1, TEST2 oraz PROD ma odwzorowanie w GIT tak aby kody z GIT pokrywały się z bazą. No i dochodzimy do sedna problemu. Nowy projekt to nowy biznes, który z przyczyn niezależnych od nas nie ma dostępu do TEST2 i mieć nie będzie. Zatem jakaś mądra głowa wymyśliła kolejne środowisko TEST3. Zupełnie niezależne od wszystkiego z dostępem dla nowego biznesu. Środowisko z pełnymi testami E2E. No dobra ale jak to wpiąć w normalny nasz proces wydawniczy aby sobie kodów potem na prod nie nadpisywać?
Ustaliłem z PM'em i architektem, że Ci 2 nowi developerzy będą developować na tym środowisku TEST3. Jak im biznes potwierdzi, że działa poprawnie to te zmiany dev przeniosą na nasze normalne środowiska czyli DEV (tylko dla formalności), TEST1 (do testów QA) oraz TEST2 ale tu już biznes nic nie robi bo mamy potwierdzenie na TEST3 i idziemy na PROD. Nie jest idealnie ale tak jak wspomniałem wcześniej nie da się inaczej kompromis nigdy nikogo nie zadowala w pełni.
Po mniej więcej pół roku to środowisko TEST3 w końcu powstało. Ja się z projektu już urwałem bo były inne pilniejsze tematy, a dziś na spotkaniu zespołowym (wszyscy developerzy PL/SQL z całego zespołu) mówią nam, że od teraz po testach na TEST2 mamy te zmiany z TEST2 (nie związane z powyższym projektem) wgrywać też na TEST3 tak aby na TEST3 były wszystkie zmiany i pokrywało się to z prodem. No to pierwsze 15 min wytykałem im dziury w tym podejściu, że jeden projekt będzie musiał czekać na drugi, że retesty, że rozjazd kodów, że brak nawet repo pod TEST3 i gdy w końcu się już wystarczająco "wyżyłem" pytam kto to tak spier...lił? No i odzywa się mój kierownik, że przecież było spotkanie i było ustalone. No to pytam kto był na tym spotkaniu i dostaję listę:
I tak o to 11 osób bez absolutnie ŻADNEJ wiedzy na temat procesu przyszło i wymyśliło nowy proces nowe flow i jeszcze nas poinformować zapomnieli. Serio rozglądam się czy gdzieś jakaś kamera nie wisi i ktoś nie ciśnie ze mnie beki w jakiejś ukrytej kamerze
ma odwzorowanie w GIT tak aby kody z GIT pokrywały się z bazą.
Nie potrafię sobie wyobrazić czemu tak to działa i jaki problem to rozwiązuje, ale to chyba klucz do tego problemu
Padły terminy pl/sql i oracle, więc zakładam, że w gicie trzymany jest kod jakichś procedur bazodanowych.
Z cyklu #absurdyCorpo
Z dokumentacji oracle można wyczytać:
Using Compound Triggers to Avoid Mutating-Table Error
You can use compound triggers to avoid the mutating-table error (ORA-04091) described in Trigger Restrictions on Mutating Tables.Scenario: A business rule states that an employee's salary increase must not exceed 10% of the average salary for the employee's department. This rule must be enforced by a trigger.
Solution: Define a compound trigger on updates of the table hr.employees, as in Example 9-4. The state variables are initialized each time the trigger fires (even when the triggering statement is interrupted and restarted).
No więc w firmie gdzie pracuję jeden z triggerów mutował. Ktoś sobie doczytał, że jest coś takiego jak compound przerobił triggera i problemu nie ma ... no ale nie uwzględnił tego, że trigger na tabeliA robi insert do tabeliB, następnie uruchamiany jest pakiet, który robi update na tabeliB, trigger na tabeliB (compound) odpala inny pakiet gdzie jest select z tabeliA i uwaga ...:
Sprawdzenie dla klienta : xxx nieudane z powodu bledu ORA-04091: tabela xxx ulega mutacji, wyzwalacz/funkcja może tego nie widzieć
Tak to jest robić logikę na triggerach ...
Złe zaprojektowanie i myślenie krótkoterminowe z utrzymaniem projektu. Do tego pewnie osoba która to tworzyła już nie pracuje więc wiedza domenowa wyparowała. Klasyczne firmy IT.
Z cyklu ciekawostki #FireDAC
Na wstępie chciałbym uniknąć zbędnej dyskusji, że delphi umarło itd. Załączam jedynie jako ciekawostkę / info jeśli ktoś korzysta.
Mam kawałek kodu, który bierze DDL obiektu oracle (procedura/funkcja/pakiet/trigger itp.) z pliku i wgrywa ją na docelowe środowisko. Jak to w przypadku oracle bywa jak coś jest nie tak to status obiektu jest invalid i np w przypadku pakietów gdzie wgrywany jest np tylko header pakietu to wymagana jest kompilacja body tegoż pakietu bo automatycznie wchodzi w stan invalid.
W związku z powyższym po wgraniu czegokolwiek na bazkę uruchomiłem poniższy kod:
BEGIN
FOR CUR IN (SELECT * FROM ALL_OBJECTS
WHERE OWNER = :USER
and STATUS = 'INVALID'
and OBJECT_TYPE <> 'JAVA CLASS'
and OBJECT_NAME = :OBJECT_NAME) LOOP
BEGIN
IF CUR.OBJECT_TYPE = 'PACKAGE BODY' THEN
EXECUTE IMMEDIATE 'ALTER PACKAGE "'||CUR.OWNER||'"."'||CUR.OBJECT_NAME||'" COMPILE BODY';
ELSE
EXECUTE IMMEDIATE 'ALTER '||CUR.OBJECT_TYPE||' "'||CUR.OWNER||'"."'||CUR.OBJECT_NAME||'" COMPILE';
END IF;
END;
END LOOP;
END;
który automatycznie kompiluje mi dany obiekt/obiekty.
Wywołanie natomiast powyższego kodu wygląda tak:
for var k: integer := 0 to FFilList.Count - 1 do
begin
if TdmMainModule.Instance.LoadScriptFromFileFD(FFilList[k].value) then
try
TdmMainModule.Instance.qScript.ExecuteAll;
if ((FFilList[k].objectType <> TObjectEnum.oeOthers) and (pAutoCompile)) then
TryCompileObject(FFilList[k].value); //tu jest execute powyższego PL/SQL'a
if Assigned(fOnGetText) then
fOnGetText('OK: ' + FFilList[k].value + ' ' + sAddInfo);
except
on e: Exception do
if Assigned(fOnGetText) then
fOnGetText('Fail: ' + FFilList[k].value + ' ' + e.Message);
end;
end;
No i mam wszystko OK
Co ciekawe na bazie jest status pakietu INVALID no i rozkmina dlaczego. Odpaliłem PL/SQL'a z toad i zwrócił mi:
ORA-24344: Success with Compilation Error
niby jakiś tam ORA ale do exceptiona nie wchodzi .... i tu niestety bez wczytania się w kody FireDAC nie dało rady. Najpierw FireDac.Phys.OracleCli.pas
OCI_SUCCESS = 0; // maps to SQL_SUCCESS of SAG CLI
OCI_SUCCESS_WITH_INFO = 1; // maps to SQL_SUCCESS_WITH_INFO
OCI_RESERVED_FOR_INT_USE = 200; // reserved
OCI_NO_DATA = 100; // maps to SQL_NO_DATA
OCI_ERROR = -1; // maps to SQL_ERROR
OCI_INVALID_HANDLE = -2; // maps to SQL_INVALID_HANDLE
OCI_NEED_DATA = 99; // maps to SQL_NEED_DATA
OCI_STILL_EXECUTING = -3123; // OCI would block error
następnie FireDAC.Phys.OracleWrapper.pas
procedure TOCIError.Check(ACode: sword; AInitiator: TObject = nil);
begin
if FIgnoreErrors then
Exit;
case ACode of
OCI_SUCCESS:
;
OCI_NEED_DATA,
OCI_INVALID_HANDLE,
OCI_STILL_EXECUTING,
OCI_CONTINUE:
FDException(AInitiator, EOCINativeException.Create(Self, ACode, '')
{$IFDEF FireDAC_Monitor}, Env.Tracing {$ENDIF});
OCI_SUCCESS_WITH_INFO:
begin
ClearInfo;
FInfo := EOCINativeException.Create(Self);
end;
OCI_NO_DATA,
OCI_ERROR:
FDException(AInitiator, EOCINativeException.Create(Self)
{$IFDEF FireDAC_Monitor}, Env.Tracing {$ENDIF});
else
;
end;
end;
Jak widać OCI_SUCCESS_WITH_INFO jest jedynym exceptionem, który nie tworzy FDException ... no taaa logiczne przecież
@robertos7778: za długo trwa dlatego wyszukuję tylko obiekty rozkompilowane, które wgrywałem :)
Z cyklu #AbsurdyCorpo
Jak to zwykle bywa godzina 15:50 ja już spakowany lapek w plecaku jeszcze tylko poszedłem kubek od kawy umyć... wracam do biurka a tu kierownik czeka i pyta do której pracuję? Mówię, że normalnie do 16, a on zaczyna, że jest critical na produkcji, że wszystko leży i kwiczy, awaria z naciskiem z najwyższego szczebla, że prezes zarządu dzwoni i w ogóle świat się kończy. Pytam co się stało, a on, że w sumie to nie jest pewien i prześle mi korespondencję abym się tym zajął. Mówię mu, że spoko ale muszę teraz jechać do domu i za godzinkę do tego usiąde.... Wracam do domu otwieram te maile a tam pierwsza wzmianka o jakichś problemach z przed 4 dni ... myślę co to za critical skoro od 4 dni jest błąd i nie jest poprawiony o_0 ...
No ale nic przesiedziałem prawie dwie godziny na czytaniu maili z biznesowym bełkotem, którego nie rozumiem napisałem maila, że potrzebuję kogoś od strony technicznej (admina czy developera od tego systemu - bo to nie ten przy którym ja robię) aby się dogadać o co tak naprawdę chodzi. Drugiego dnia rano dzwoni analityk biznesowy i opowiada, że system, w którym się wykorbiło zmienił serwer, a dokładnie przeszli na nowy serwer o_0 okazuje się bowiem, że nowy serwer inaczej interpretuje pewne dane stąd problemy ... aby ich uniknąć to ja w systemie przy którym pracuję (a dokładniej baza danych oracle) muszę przygotować nowy raport, który będzie wkładem dla systemu, który się wykorbił aby oni na tej podstawie stwierdzili co i jak przesyłać do nowego serwera.
Co ciekawe ja nawet nie wiedziałem, że u nich jakaś aktualizacja była .... no bo po co informować backendowców, że zmieniają się serwery/protokoły komunikacyjne/usługi ...
Standard, u mnie klient zgłasza coś, PM zastanawia się przez 2-6 tygodni co zostało zgłoszone i wreszcie trafia na szczyt backlogu. W większości przypadków od razu zostaje zamknięte, bo ktoś to już zrobił przy okazji i tylko czekało aż PM dostanie zjebki od klienta, żeby to ustawić odpowiednio wysoko.
Z cyklu ciekawostki #oracle
Pokażę wam ciekawostkę jaka wyszła dopiero na produkcji :)
Zagraniczny klient preferuje wymianę danych plikową. Ma swój konkretny format pliku z konkretną liczbą znaków w danej linii itd. Natomiast pierwsze dwa znaki w linii oznaczają co to w tej linii dalej będzie.
Przykładowo
00NAZWAKLIENTA
01ADRESKLIENT
02OSOBAKONTAKTOWA
...
99PODSUMOWANIE
i skupmy się na tym podsumowaniu :)
99 oznacza ostatni wiersz w pliku ze swojego rodzaju podsumowaniem ilości rekordów wprowadzonych wcześniej.
Cały plik generowany był po stronie oracle procedurą bazodanową jak poniżej:
rpad('99' || v_ile_rekordow_wstawiono + 2,1399,' ')
v_ilre_rekordow_wstawiono to ilość rekordów + nagłówek i aktualny rekord z podsumowaniem (+2) i oczywiście uzupełniamy spacjami do końca 1399 znaków
Na środowiskach testowych gdzie danych było niewiele ostatnia linia wyglądała mniej więcej tak
991... (na dev)
9930... (na środowisku testowym dla developerów)
9950... (na środowisku testerów)
czyli prefix 99 + ilość rekordów.
Natomiast na produkcji zamiast 99xxx... pojawiło się 10001
Wynika to z prostej przyczyny, że użyty został znak || zamiast np concat
okazuje się, że oracle ogólnie łączy || ale w momencie kodu jak powyżej najpierw '99' zamienia na wartość liczbową a dopiero potem wykonuje operacje matematyczne, a dzieje się to przez ten +2 na końcu :) Przykład poniżej:
select to_char('99'||1) from dual
union all
select to_char('99'||1+2) from dual
union all
select concat('99',1+2) from dual
/
daje wynik:
Nagłówek 1 |
---|
991 |
993 |
993 |
natomiast jak 1 zamienimy na 99
select to_char('99'||101) from dual
union all
select to_char('99'||99+2) from dual
union all
select concat('99',99+2) from dual
/
wyniki jakie dostajemy to już:
Nagłówek 1 |
---|
99101 |
10001 |
99101 |
Zatem przestrzegam przed używaniem operatorów matematycznych przy łączeniu varcharów za pomocą ||
Jak się zacznie czytać wstęp o formacie, to można flashbacków z Advent of Code dostać ( ͡° ͜ʖ ͡°)
@several: wiadomo, że wszystko da się zrobić, choć zadając to pytanie właśnie myślałem sobie o tych patchach do linuxa jako kontrprzykład tego jak ciężkie jest to do zrobienia xd
Ujmując to inaczej: jak projekt rozwijany przez mnóstwo ludzi nie używający zewnętrznych bibliotek i napisany w prostym C jest tak ciężki do zoptymalizowania to jakie szanse są na podobny zabieg w dużym projekcie C++ rozwijanym przez 10 devów, gdzie nawet zwykły include z biblioteki standardowej kosmicznie podbija czasy kompilacji