1c 8 transakcji ze źródeł zewnętrznych. Klienci jednorazowi. Segmentacja w celu uzyskania powtarzających się zakupów. Koszt towaru w zamówieniu

Niezależnie od wybranej opcji działania (plik czy klient-serwer), system 1C:Enterprise zapewnia pracę z informacjami zgromadzonymi w bazie danych z wykorzystaniem mechanizmu transakcyjnego.

Transakcja- jest to niepodzielna sekwencja operacji manipulacji danymi z punktu widzenia wpływu na bazę danych. Działa na zasadzie „wszystko albo nic” i przenosi bazę danych z jednego stanu holistycznego do innego stanu holistycznego. Jeżeli z jakiegoś powodu jedna z akcji transakcyjnych nie będzie możliwa do wykonania lub nastąpi jakieś zakłócenie działania systemu, baza danych powraca do stanu sprzed rozpoczęcia transakcji (transakcja zostaje wycofana).

System 1C:Enterprise domyślnie wywołuje transakcje podczas wykonywania jakichkolwiek działań związanych z modyfikowaniem informacji przechowywanych w bazie danych. Na przykład w transakcji wywoływane są wszystkie procedury obsługi zdarzeń znajdujące się w modułach obiektów i zestawu rekordów związane z modyfikacją danych bazy danych. Transakcja odczytuje także obiekty następujących typów: Obiekt Planobena, Dokument -temat statku powietrznego, książeczka referencyjna, Planwid - Obliczenia Charakterów i Planwordów, Plan - Licznik -temat, Procedura Tablicy, Obiekt, Wyciskacze, Rejestr Rejestrów, Rejestr -Knock -Racing, Registachgontinarinapers, Rejestracja Rejestracji, Ponowne raportowanie , nieprzewidywalny.Rekordy. W takim przypadku w trybie zarządzanego blokowania instalowana jest wspólna blokada według wartości rejestru dla zestawów rekordów i wartości wyboru dla zestawu rekordów niezależnego rejestru informacyjnego.

Oprócz tego programista może jawnie pracować z transakcjami. W tym celu należy skorzystać z procedur kontekstu globalnego StartTransaction(), CommitTransaction() i CancelTransaction().

Korzystanie z jawnego wywołania transakcji

metoda Rozpocznij transakcję() pozwala na otwarcie transakcji. Wszelkie zmiany w informacjach w bazie danych wprowadzone w kolejnych instrukcjach można następnie całkowicie zaakceptować lub całkowicie odrzucić. Aby zaakceptować wszystkie wprowadzone zmiany należy skorzystać z metody Zatwierdź transakcję(). Aby cofnąć wszystkie zmiany dokonane w otwartej transakcji należy skorzystać z metody Anuluj transakcję(). Jeśli liczba wywołań metod Rozpocznij transakcję() przekracza liczbę wywołań metod Zatwierdź transakcję() Lub Anuluj transakcję(), wówczas system wykona niejawne wywołanie metody Anuluj transakcję() w następujących przypadkach:

● na końcu wykonania wbudowanego języka (obsługa zdarzeń, połączenie zewnętrzne, serwer automatyzacji);

● podczas przekazywania kontroli z serwera na klienta.

Jeśli liczba wywołań metod Zatwierdź transakcję () Lub Anuluj transakcję() przekracza liczbę wywołań metod Rozpocznij transakcję(), a następnie podczas wykonywania niepotrzebnego wywołania metody Zatwierdź transakcję() Lub Anuluj transakcję() zostanie zgłoszony wyjątek. Zatem schemat pracy z transakcją w ogólna perspektywa może wyglądać tak:

Próba

Rozpocznij transakcję();

// Sekwencja instrukcji

Zatwierdź transakcję();

Wyjątek

Anuluj transakcję();

Zakończ próbę;

Stosując taki schemat należy pamiętać, że nie wszystkie błędy pojawiające się podczas pracy z bazą danych są przetwarzane przez system w ten sam sposób. Ogólnie wszystkie błędy baz danych można podzielić na dwie kategorie:

● nie do odzyskania,

● możliwe do odzyskania.

Błędy nie do naprawienia- są to błędy, jeżeli wystąpią, normalne funkcjonowanie systemu 1C:Enterprise może zostać zakłócone, np. mogą zostać uszkodzone dane. Jeśli wystąpi nieodwracalny błąd, działanie systemu 1C:Enterprise zostanie w każdym przypadku zakończone. Jeżeli podczas realizacji transakcji wystąpi nieodwracalny błąd, wówczas wszelkie zmiany dokonane w ramach tej transakcji zostaną anulowane przez system.

Błędy możliwe do naprawienia- są to błędy, które nie powodują poważnych zakłóceń w działaniu systemu 1C:Enterprise. Jeśli wystąpi możliwy do naprawienia błąd, dalsza praca systemu może być kontynuowana. W tym przypadku oczywiście sama operacja, która spowodowała błąd, zostaje zakończona i zgłoszony zostaje wyjątek, który może zostać przechwycony i przetworzony przez konstrukcję

Próba... Wyjątek... EndTry.

Zagnieżdżone wywołanie transakcji

W ramach trwającej już transakcji możesz uzyskać dostęp do procedur Rozpocznij transakcję(), Zatwierdź transakcję() I Anuluj transakcję(). Można na przykład zastosować następujący wzorzec wywołania:

Rozpocznij transakcję();

Rozpocznij transakcję();

Zatwierdź transakcję();

// Zagnieżdżone wywołanie transakcji

Rozpocznij transakcję();

Zatwierdź transakcję();

Zatwierdź transakcję();

Jednak takie wywołanie nie oznacza rozpoczęcia nowej transakcji w ramach już trwającej.

UWAGA!System 1C:Enterprise nie obsługuje transakcji zagnieżdżonych.Oznacza to, że zawsze ważna jest tylko sama transakcja. Najwyższy poziom.

Wszystkie transakcje wywoływane w ramach już otwartej transakcji są w rzeczywistości częścią tej samej transakcji, a nie tworzą transakcję zagnieżdżoną. Zatem cofnięcie zmian dokonanych w transakcji zagnieżdżonej ostatecznie nie cofnie zmian w samej transakcji zagnieżdżonej, ale raczej cofnie wszystkie zmiany w transakcji najwyższego poziomu. Jednocześnie zmiany zatwierdzeń dokonane w transakcji zagnieżdżonej są ignorowane.

Wpływ transakcji na działanie obiektów oprogramowania

Ogólnie rzecz biorąc, obiekty oprogramowania używane przez system 1C:Enterprise są całkowicie przezroczyste dla transakcji w bazie danych. Innymi słowy, transakcje bazy danych można wywołać podczas wykonywania różne metody obiekty oprogramowania jednak na przykład działania wykonywane przez bazę danych podczas wycofywania transakcji zazwyczaj nie wpływają na odpowiednią transakcję oprogramowanie obiekty.

Wynika z tego, że anulując transakcje w bazie danych, programista (jeśli to konieczne) musi samodzielnie zapewnić odpowiednie zmiany danych odpowiedniego oprogramowanie obiekty. Można tego dokonać poprzez ponowne odczytanie wszystkich danych obiektu lub zmianę niektórych szczegółów obiektu programu.

Istnieją wyjątki od tej reguły. Ze względu na znaczną specyfikę aplikacji obiektów oprogramowania systemu 1C:Enterprise, w niektórych przypadkach wycofanie zmian dokonanych w bazie danych może nadal wpływać na wartości właściwości odpowiednich oprogramowanie obiekty. Dzieje się tak w następujących przypadkach:

● w przypadku anulowania transakcji atrybut księgowania dokumentu przywraca wartość sprzed rozpoczęcia transakcji;

● jeżeli obiekt został utworzony i zapisany w transakcji, to w momencie wycofania transakcji wartość referencyjna zostaje wyzerowana;

● jeżeli obiekt powstał poza transakcją i przy zapisywania go w transakcji został użyty automatycznie wygenerowany kod/numer, to w przypadku anulowania transakcji kod/numer zostanie skasowany.

2017-08-12

Twórz niestandardowe transakcje w celu utrzymania obiektów OM.

Wstęp

Myślę, że wielu funkcjonalnych konsultantów SAP spotkało się z transakcją utrzymywania obiektów zarządzania organizacją. Mianowicie transakcja PP01

Użycie tej transakcji zapewnia użytkownikowi możliwość administrowania infotypami zarządzania organizacją dla tych typów obiektów, które są używane w automatyzowanych procesach biznesowych. Bardzo często transakcja ta wykorzystywana jest jako pojedynczy punkt wejścia do pracy ze wszystkimi typami obiektów zarządzania organizacją, co w rzeczywistości nie jest zbyt dobrą praktyką. No cóż, albo niezbyt wygodne. Chociaż z pewnością powszechne. Następnie postaram się powiedzieć, jaka może być alternatywa.

Tabela T77S0, grupa „TCODE”

Podczas konfigurowania obiektów obiektów OM prawdopodobnie dotkniesz ustawienia znajdującego się w następującej ścieżce SPRO:

IMG: Zarządzanie personelem -> Zarządzanie organizacją -> Ustawienia podstawowe -> Udoskonalanie modelu danych -> Obsługa typów obiektów

Tutaj tworzysz nowe obiekty OM, wymyślasz dla nich nazwy, wybierasz ikony i definiujesz dla nich pewne ustawienia... W tej chwili interesuje nas węzeł " Klucz typu obiektu + transakcja"

Część widoku konfiguracji otworzy się przed Tobą T77S0 z filtrowanymi wartościami grupowymi

Warto zwrócić uwagę na grupę TKOD w którym, jeśli przyjrzysz się uważnie, znajdziesz techniczne nazwy transakcji, z którymi najprawdopodobniej miałeś do czynienia. Co więcej, w kolumnie Wartość wskazuje rodzaj przedmiotu, dla którego przeznaczona jest dana transakcja.

Co jest specjalnego w tych transakcjach?

Korzystając z transakcji, które mają na celu obsługę określonego typu obiektu, nie musisz już wybierać tych właśnie typów obiektów, które są domyślnie dostępne w transakcji PP01. Czyli poprzez uruchomienie np. transakcji PO09, natychmiast zaczynasz pracować z obiektami takimi jak L

Tworzenie nowej transakcji dla własnego obiektu zarządzania organizacją

W jednym z moich poprzednich postów mówiłem o tym, jak utworzyć nowy obiekt OM + dodać do niego wyszukiwanie strukturalne

Nie odstąpię daleko od tego materiału. W ramach demonstracji utworzę nową transakcję, aby utrzymać obiekt 91.

Definiowanie nowego typu obiektu w T77S0

Zdefiniuj nazwę przyszłej transakcji w widoku ustawień T77S0

Wartość „ZP91M” w tym przypadku jest nazwą przyszłej transakcji na utrzymanie obiektu 91 . Zapisz zmiany.

Tworzenie nowej transakcji w celu obsługi obiektu OM

Poprzez transakcję SE93 utwórz transakcję, aby utrzymać swój obiekt. Poniżej fragment wideo przedstawiający sekwencję czynności, które należy wykonać, aby utworzyć odpowiednią transakcję

Zanotuj wartości, które zostały użyte w polach Program, Numer ekranu,Obiekt autoryzacyjny. Teraz rozpocznij nową transakcję

Użytkownik ma możliwość pracy tylko z określonym rodzajem obiektu, co w pewnym sensie można nazwać wygodą i, jeśli chcesz, minimalizacją dodatkowych działań w celu wybrania pożądanego obiektu.

Ostatni raz, kiedy patrzyliśmy najprostszy sposób za pomocą wbudowanego języka 1C. Na praktyce transakcje znacznie częściej używane w połączeniu z projektem. Pozwala to w przypadku wystąpienia błędu na kontynuację wykonywania kodu, a także na przesłanie użytkownikowi odpowiedniego komunikatu o błędzie oraz zapisanie informacji do dziennika rejestracji lub pliku dziennika w celu późniejszej analizy przez administratora systemu.

Jeśli przejdziemy do dokumentacji technicznej lub dysku ITS, zobaczymy, że 1C zaleca następującą metodę organizacji transakcji

Próba //1. Początek transakcji. Rozpocznij transakcję() ; //2. Blok operacji wykonywanych w transakcji. //3. Jeśli wszystkie operacje przebiegną pomyślnie, zatwierdzamy transakcję. Zatwierdź transakcję() ; Wyjątek //4. Jeśli podczas wykonywania kodu wystąpią błędy, anuluj transakcję. Anuluj transakcję() ; //5. W razie potrzeby zapisz w dzienniku pokładowym. //6. W razie potrzeby wyświetl komunikat użytkownikowi. Zakończ próbę ;

Właściwie kod nie wymaga specjalnego wyjaśnienia. Jeśli w toku próbowanie Podczas wykonywania kodu transakcyjnego pojawia się błąd, od razu wpadamy w blok wyjątek, tj. przed metodą Zatwierdź transakcję() po prostu tam nie dotrzemy. Cóż, w drodze wyjątku, odpowiednio anulujemy transakcję i jeśli to konieczne, wyświetlimy komunikat o błędzie i zapiszemy informację do logu. Bardzo pożądane jest zapisywanie błędów w dzienniku pokładowym, zwłaszcza w przypadku operacji wykonywanych bez udziału użytkownika (na przykład rutynowe zadania). Umożliwi to późniejszą analizę błędu. Zamiast logowania możesz ustawić wysyłanie wiadomości do administratora e-mailem.

Teraz uzbrojeni w nową wiedzę spróbujmy zmodyfikować kod omówiony w artykule o . Przypomnę, że rozważaliśmy wpis w katalogu Dobra oraz do rejestru informacyjnego Cena według następującego schematu:

&Na serwerzeBez kontekstu Rozpocznij transakcję() ; //nagraj nowy produkt Produkt = Katalogi. Dobra. Utwórz element() ; Produkt. Nazwa = „Dziurkacz”; Produkt. Pisać() ; //zapisz cenę RecordSet = Rejestry informacyjne. Cena. UtwórzZestawRekordów() ; NowyRekord = Zestaw Rekordów. Dodać() ; Nowy rekord. Okres = bieżąca data() ; Nowy rekord. Produkt = Produkt. Połączyć; Nowy rekord. Kwota = 100 ; Zestaw rekordów. Pisać() ; Zatwierdź transakcję() ; Koniec procedury

Teraz umieśćmy transakcję w bloku Próba wyjątku. Najprawdopodobniej błędy mogą wystąpić tylko przy zapisie do książki telefonicznej lub rejestru informacyjnego, tzw wstępne przygotowanie Wyjdźmy poza transakcję.

&Na serwerzeBez kontekstu Procedura RunTransactionOnServer() //utwórz nowy produkt Produkt = Katalogi. Dobra. Utwórz element() ; Produkt. Nazwa = „Dziurkacz”; //Utwórz rekord z ceną RecordSet = Rejestry informacyjne. Cena. UtwórzZestawRekordów() ; NowyRekord = Zestaw Rekordów. Dodać() ; Nowy rekord. Okres = bieżąca data() ; Nowy rekord. Kwota = 100 ; //Wykonaj transakcję podczas próby Próba rozpoczęcia transakcji(); Produkt. Pisać() ; Nowy rekord. Produkt = Produkt. Połączyć; Zestaw rekordów. Pisać() ; Zatwierdź transakcję() ; Wyjątek AnulujTransakcję() ; Wiadomość = Nowa wiadomość do użytkownika; Wiadomość. Tekst = ; Wiadomość. Zgłosić() ; Rejestracja dziennika ( „Wystąpił błąd podczas zapisywania produktu i jego ceny”) ; Zakończ próbę ; Koniec procedury

Czego nie robić

Ci, którzy dopiero zaczynają pracować z transakcjami, często mają ochotę zrobić to w ten sposób

Rozpocznij transakcję() ; Próba rozpoczęcia transakcji(); //Blok operacji CommitTransaction() ; Wyjątek AnulujTransakcję() ; Zakończ próbę ; Próba rozpoczęcia transakcji(); //Blok operacji CommitTransaction() ; Wyjątek AnulujTransakcję() ; Zakończ próbę ; Zatwierdź transakcję() ;

Lub w pętli

Rozpocznij transakcję() ; Dla każdej pętli danych z tablicy danych próba uruchomienia transakcji() ; Dane. Pisać() ; Zatwierdź transakcję() ; Wyjątek AnulujTransakcję() ; Zakończ próbę ; Koniec cyklu ; Zatwierdź transakcję() ;

Na pierwszy rzut oka zrobiliśmy wszystko zgodnie z zaleceniami firmy 1C. Ale faktem jest, że platforma 1C nie obsługuje transakcji zagnieżdżonych. Oznacza to, że technicznie rzecz biorąc, można tak pisać. Ale jednocześnie wszystkie zagnieżdżone transakcje nie tworzą nowych, ale należą do tej samej transakcji najwyższego poziomu. W ten sposób, jeśli jedna z zagnieżdżonych transakcji zakończy się niepowodzeniem, następna zagnieżdżona transakcja nie może zostać zatwierdzona. System wyświetli komunikat typu: „W tej transakcji wystąpiły już błędy!”. Pokażmy to na przykładzie. Załóżmy, że decydujemy się zarejestrować dwa towary, każdy w ramach własnej transakcji. I zagnieźdźmy te transakcje w trzecim. Następnie sztucznie wywołamy błąd w pierwszej transakcji tą metodą Podnieś wyjątek:

&Na serwerzeBez kontekstu Procedura RunTransactionOnServer() StartTransaction() ; Próba rozpoczęcia transakcji(); Produkt = Katalogi. Dobra. Utwórz element() ; Produkt. Nazwa = "Tabela" ; Produkt. Pisać() ; Podnieś wyjątek „Błąd we wpisie produktu.”; Zatwierdź transakcję() ; Wyjątek AnulujTransakcję() ; Wiadomość = Nowa wiadomość do użytkownika; Wiadomość. Tekst = Opis błędu() AttemptStartTransaction() ; Produkt = Katalogi. Dobra. Utwórz element() ; Produkt. Imię = "Krzesło" ; Produkt. Pisać() ; Zatwierdź transakcję() ; Wyjątek AnulujTransakcję() ; Wiadomość = Nowa wiadomość do użytkownika; Wiadomość. Tekst = Opis Błędu() ; Wiadomość. Zgłosić() ; Zakończ próbę ; Zatwierdź transakcję() ; Koniec procedury

W wyniku wykonania tej procedury w oknie komunikatu zobaczymy:

(ExternalProcessing.TransactionsAtTrying.Form.Form.Form(20)): Błąd podczas zapisywania elementu. (ExternalProcessing.TransactionsAtTrying.Form.Form.Form(40)): Błąd podczas wywoływania metody kontekstowej (Zapisz): W tej transakcji wystąpiły już błędy!

Zatem organizowanie zagnieżdżonych transakcji w 1C jest absolutnie bezcelowe.

Możliwe opcje

Wróćmy teraz do opcji, w której zapisaliśmy produkt i jego cenę. Jeśli podczas dokonywania transakcji wystąpi błąd, trudno będzie zrozumieć, w którym momencie on nastąpił – podczas zapisywania produktu czy podczas zapisywania ceny, gdyż jedno i drugie następuje w ramach tej samej próby. Aby określić, gdzie wystąpił błąd, każdą operację zapisu musimy zakończyć osobno i unikać transakcji zagnieżdżonych. Aby to zrobić, wprowadzamy zmienną logiczną Odmowa i w zależności od jej wartości na koniec wszystkich operacji zatwierdzimy lub anulujemy transakcję.

&Na serwerzeBez kontekstu Procedura RunTransactionOnServer() // Rozpocznij transakcję Odmowa = Fałsz; Rozpocznij transakcję() ; // Próbuję nagrać produkt Próba produktu = katalogi. Dobra. Utwórz element() ; Produkt. Nazwa = „Dziurkacz”; Produkt. Pisać() ; Wyjątek Błąd = Prawda; Wiadomość = Nowa wiadomość do użytkownika; Wiadomość. Tekst = „Błąd podczas nagrywania produktu”; Wiadomość. Zgłosić() ; Zakończ próbę ; // Próbuję zapisać cenę AttemptRecordSet = Rejestry informacyjne. Cena. UtwórzZestawRekordów() ; NowyRekord = Zestaw Rekordów. Dodać() ; Nowy rekord. Okres = bieżąca data() ; Nowy rekord. Produkt = Produkt. Połączyć; Nowy rekord. Kwota = 100 ; Zestaw rekordów. Pisać() ; Wyjątek Błąd = Prawda; Wiadomość = Nowa wiadomość do użytkownika; Wiadomość. Tekst = „Błąd podczas zapisywania ceny”; Wiadomość. Zgłosić() ; Zakończ próbę ; // Zatwierdź lub anuluj transakcję Jeśli NIE nastąpi błąd, wówczas CommitTransaction() ; Inaczej Anuluj Transakcję() ; KoniecJeśli ; Koniec procedury

To samo możemy zrobić, iterując i zapisując dowolne dane w pętli. W takim przypadku będziemy mogli uzyskać listę wszystkich danych z ewentualnymi błędami.

Przygotowując się do certyfikacji 1C Expert, w przededniu dwóch bardzo ważnych i globalnych tematów - blokowania, chciałbym przyjrzeć się czemuś, bez czego powyższe koncepcje są niemożliwe - transakcji DBMS.

Transakcja- logicznie powiązany, niepodzielny ciąg działań. Transakcja może zostać zrealizowana w całości lub w ogóle. Do zatwierdzenia transakcji w systemie DBMS używana jest metoda COMMIT.

Typowym przykładem transakcji jest przelew Pieniądze z jednego konta na drugie:

  1. rozpocząć transakcję;
  2. odczytaj kwotę środków na koncie nr 123;
  3. zmniejsz saldo konta 123 o 100 rubli;
  4. zapisz saldo konta nr 123;
  5. odczytaj kwotę środków na koncie nr 321;
  6. zwiększ swoje saldo o 100 rubli;
  7. zapisz nową kwotę środków na koncie 321;
  8. zatwierdzić transakcję.

Uzyskaj 267 lekcji wideo na 1C za darmo:

Jak widzimy, jeśli transakcja nie zostanie całkowicie sfinalizowana, to nie ma ona żadnego znaczenia.

Kluczowe wymagania (ACID) dla transakcyjnego systemu DBMS

Jednym z najbardziej powszechnych zestawów wymagań dla transakcji i transakcyjnych systemów DBMS jest zestaw ACID (atomowość, spójność, izolacja, trwałość). Oto właściwości, które musi posiadać każda transakcja:

  • Atomowość— żadna transakcja nie powinna być rejestrowana częściowo;
  • Konsystencja- system jest w stanie spójnym przed rozpoczęciem transakcji i musi pozostać w spójnym stanie po zakończeniu transakcji;
  • Izolacja— w trakcie realizacji transakcji transakcje równoległe nie powinny mieć wpływu na jej wynik;
  • Trwałość- w przypadku niepowodzenia zmiany wprowadzone w wyniku pomyślnie zakończonej transakcji muszą pozostać zapisane po powrocie systemu do pracy.

Transakcje w 1C

Transakcje w 1C 8.3 i 8.2 są tworzone automatycznie i opisywane przez programistów.

Aby sprawdzić, czy transakcja jest aktywna, możesz użyć metody TransactionActive().

Przykładem transakcji automatycznej jest przetworzenie księgowania dokumentu, zapisanie pozycji katalogowej do bazy danych, zapisanie zbioru rekordów rejestru informacyjnego itp.

Tytuł był chwytliwy, ale się rozkręcił. Od razu powiem, że będziemy mówić o 1C. Drodzy użytkownicy 1C, nie wiecie, jak pracować z transakcjami i nie rozumiecie, jakie są wyjątki. Doszedłem do tego wniosku przeglądając duża liczba Kod 1C, zrodzony w dziczy krajowego przedsiębiorstwa. W typowe konfiguracje to wszystko jest wystarczająco dobre, ale przerażająca ilość niestandardowego kodu jest napisana niekompetentnie z punktu widzenia bazy danych. Czy kiedykolwiek widziałeś błąd „Ta transakcja napotkała już błędy”? Jeśli tak, to tytuł artykułu dotyczy także Ciebie. Zastanówmy się w końcu, jakie są transakcje i jak poprawnie je obsługiwać podczas pracy z 1C.

Dlaczego powinniśmy włączyć alarm?

Najpierw ustalmy, czym jest błąd „Wystąpiły już błędy w tej transakcji”. W rzeczywistości jest to niezwykle prosta rzecz: próbujesz pracować z bazą danych w ramach już wycofanej (anulowanej) transakcji. Na przykład gdzieś została wywołana metoda CancelTransaction i próbujesz ją zatwierdzić.


Dlaczego to jest złe? Ponieważ ten błąd nie mówi nic o tym, gdzie faktycznie wystąpił problem. Gdy support otrzyma od użytkownika zrzut ekranu z takim tekstem, a zwłaszcza dla kod serwera, z którym człowiek nie współpracuje interaktywnie to... Chciałem napisać „błąd krytyczny”, ale pomyślałem, że to takie modne hasło, na które nikt już nie zwraca uwagi... To jest tyłek. Jest to błąd programowy. To nie jest przypadkowa usterka. Jest to błąd, który należy natychmiast naprawić. Ponieważ gdy w nocy procesy serwera działającego w tle przestają działać, a firma zaczyna gwałtownie tracić pieniądze, ostatnią rzeczą, którą chcesz widzieć w dziennikach diagnostycznych, jest informacja „Wystąpiły już błędy w tej transakcji”.


Istnieje oczywiście możliwość, że dziennik technologiczny serwera (jest włączony na produkcji, prawda?) pomoże w jakiś sposób zdiagnozować problem, ale w tej chwili nie przychodzi mi do głowy żadna opcja - jak dokładnie znaleźć w nim prawdziwą przyczynę tego błędu. Ale prawdziwy powód jest jeden - programista Vasya otrzymał wyjątek w transakcji i zdecydował, że raz nie był to zły pomysł, „po prostu pomyśl, to błąd, idźmy dalej”.

Jakie są transakcje w 1C

Niezręcznie jest pisać o prawdach elementarnych, ale widocznie trochę będzie trzeba. Transakcje w 1C są takie same jak transakcje w DBMS. Nie są to jakieś specjalne transakcje „1C”, są to transakcje w systemie DBMS. Zgodnie z ogólną koncepcją transakcji, mogą one zostać zrealizowane w całości lub w ogóle nie zostać zrealizowane. Wszelkie zmiany w tabelach bazy danych dokonane w ramach transakcji można natychmiast cofnąć, jak gdyby nic się nie stało.


Następnie musisz zrozumieć, że 1C nie obsługuje transakcji zagnieżdżonych. W rzeczywistości nie są one obsługiwane „w 1C”, ale w ogóle nie są obsługiwane. Przynajmniej te systemy DBMS, z którymi 1C może współpracować. Na przykład transakcje zagnieżdżone nie istnieją w MS SQL i Postgres. Każde „zagnieżdżone” wywołanie StartTransaction po prostu zwiększa licznik transakcji, a każde wywołanie „CommitTransaction” po prostu zmniejsza ten licznik. To zachowanie jest opisane w wielu książkach i artykułach, ale wnioski z tego zachowania najwyraźniej nie są wystarczająco przeanalizowane. Ściśle mówiąc, w SQL istnieje tzw. SAVEPOINT, ale 1C ich nie używa, a ta rzecz jest dość specyficzna.



Procedura Bardzo przydatny i ważny kod (Lista łączy do katalogów) StartTransaction(); Dla każdego łącza z listy łączy katalogowych Zapętl obiekt katalogu = Link.GetObject(); Directory Object.SomeField = "Zmieniono mi kod programu"; Obiekt katalogu.Write(); Koniec cyklu; Zatwierdź transakcję(); Koniec procedury

Kod w języku angielskim

Nie bardzo. Absolutnie nie chcę powielać przykładów w języku angielskim, aby rozbawić fanów świętych wojen i świętych wojen.


Prawdopodobnie piszesz taki kod, prawda? Podany przykładowy kod zawiera błędy. Co najmniej trzy. Czy wiesz które? Od razu powiem o pierwszym, związanym z blokadami obiektowymi, a nie bezpośrednio związanym z transakcjami. O drugim - trochę później. Trzeci błąd to zakleszczenie, które wystąpi podczas równoległego wykonywania tego kodu, ale to temat na osobny artykuł, nie będziemy się nim teraz zajmować, żeby nie komplikować kodu. Słowo kluczowe do googlowania: zamki sterowane zakleszczeniem.


Należy pamiętać, że kod jest prosty. Jest tego po prostu dużo w twoich systemach 1C. I zawiera co najmniej 3 błędy na raz. Pomyśl w wolnym czasie, ile błędów jest w bardziej złożonych scenariuszach pracy z transakcjami zapisanymi przez programistów 1C :)

Zamki obiektowe

Zatem pierwszy błąd. W 1C istnieją blokady obiektów, tak zwane „optymistyczne” i „pesymistyczne”. Nie wiem, kto wymyślił to określenie, zabiłbym go :). Absolutnie nie da się zapamiętać, który z nich jest za co odpowiedzialny. Zostały one szczegółowo omówione, a także w innej ogólnej literaturze informatycznej.


Istota problemu polega na tym, że w podanym przykładzie kodu obiekt bazy danych jest zmieniany, ale w innej sesji może pojawić się interaktywny użytkownik (lub sąsiedni wątek tła), który również zmieni ten obiekt. Tutaj jeden z Was może otrzymać błąd „wpis został zmodyfikowany lub usunięty”. Jeśli zdarzy się to podczas sesji interaktywnej, użytkownik podrapie się po tyłku, przeklnie i spróbuje ponownie otworzyć formularz. Jeśli dzieje się to w wątku w tle, będziesz musiał poszukać tego w dziennikach. A logbook jak wiadomo działa powoli i tylko kilka osób w naszej branży założyło stos ELK dla logów 1C... (nawiasem mówiąc, my jesteśmy w gronie tych, którzy zakładają i pomagają innym w konfiguracji :) )


Krótko mówiąc, jest to irytujący błąd i lepiej go nie mieć. Dlatego standardy deweloperskie wyraźnie stwierdzają, że przed zmianą obiektów należy nałożyć na nie blokadę obiektu za pomocą „ Obiekt katalogu.Lock()„. Wtedy sesja współbieżna (która również musi to zrobić) nie będzie mogła rozpocząć operacji aktualizacji i otrzyma oczekiwany, kontrolowany błąd.

A teraz o transakcjach

Pierwszy błąd mamy już za sobą, przejdźmy do drugiego.


Jeśli w tej metodzie nie zapewnisz sprawdzania wyjątków, wyjątek (na przykład bardzo prawdopodobny w metodzie „Write()”) wyrzuci cię z Ta metoda bez finalizacji transakcji. Wyjątek od metody „Write” może zostać zgłoszony z różnych powodów, na przykład w wyniku uruchomienia niektórych kontroli aplikacji w logice biznesowej lub wystąpienia wyżej wymienionej blokady obiektu. W każdym razie drugi błąd mówi: Kod, który rozpoczął transakcję, nie odpowiada za jej zakończenie.



Właśnie tak nazwałbym ten problem. W naszym statycznym analizatorze kodu 1C opartym na SonarQube nawet osobno wbudowaliśmy taką diagnostykę. Teraz pracuję nad jego rozwojem, a wyobraźnia programistów 1C, których kod przychodzi do mnie do analizy, czasami wprawia mnie w szok i zachwyt...


Dlaczego? Ponieważ wyjątek zgłoszony na górze transakcji w 90% przypadków nie pozwoli na zatwierdzenie tej transakcji i doprowadzi do błędu. Należy rozumieć, że 1C automatycznie wycofuje niedokończoną transakcję dopiero po powrocie z kodu skryptu na poziom kodu platformy. Dopóki jesteś na poziomie kodu 1C, transakcja pozostaje aktywna.


Przejdźmy o jeden poziom wyżej na stosie wywołań:


Procedura WażneCode() LinkList = GetLinkListWhere(); VeryUsefulAndImportantCode(LinkList); Koniec procedury

Zobacz, co się dzieje. Nasza problematyczna metoda jest wywoływana skądś z zewnątrz, wyżej na stosie. Na poziomie tej metody programista nie ma pojęcia, czy w ramach metody Bardzo Przydatnego i Ważnego Kodu będą jakieś transakcje, czy też nie. A jeśli tak, czy wszystkie zostaną ukończone... Wszyscy jesteśmy tu w imię pokoju i hermetyzacji, prawda? Autor metody „ImportantCode” nie powinien zastanawiać się nad tym, co dokładnie dzieje się wewnątrz metody, którą wywołuje. Ten sam, w którym transakcja została przetworzona niepoprawnie. W rezultacie próba pracy z bazą danych po zgłoszeniu wyjątku w ramach transakcji najprawdopodobniej zakończy się następującym komunikatem: „W tej transakcji bla bla…”

Rozkładanie transakcji pomiędzy metodami

Druga zasada kodu „bezpiecznego dla transakcji”: Liczba referencji transakcji na początku metody i na jej końcu musi mieć tę samą wartość. Nie można rozpocząć transakcji jedną metodą i zakończyć ją inną. Prawdopodobnie można znaleźć wyjątki od tej reguły, ale będzie to jakiś kod niskiego poziomu napisany przez bardziej kompetentne osoby. Generalnie nie można tak pisać.


Na przykład:


Procedura WażneCode() LinkList = GetLinkListWhere(); VeryUsefulAndImportantCode(LinkList); Zatwierdź transakcję(); // Bilet do piekła, poważna rozmowa z autorem na temat naszych skomplikowanych stosunków pracy. Koniec procedury

Powyższy kod jest niedopuszczalnym gównem. Nie można pisać metod tak, aby osoba wywołująca pamiętała i śledziła możliwe (lub prawdopodobne – kto wie) transakcje w ramach innych metod, które wywołuje. Jest to naruszenie enkapsulacji i rozprzestrzenianie kodu spaghetti, którego nie można prześledzić przy pomocy zdrowego rozsądku.


Szczególnie zabawne jest pamiętanie, że prawdziwy kod jest znacznie większy niż syntetyczne 3-liniowe przykłady. Znalezienie transakcji początkowych i końcowych na sześciu poziomach zagnieżdżenia – to bezpośrednio motywuje do intymnych rozmów z autorami.

Próbuję naprawić kod

Wróćmy do pierwotnej metody i spróbujmy to naprawić. Od razu powiem, że na razie nie będziemy naprawiać blokady obiektu, żeby nie komplikować przykładowego kodu.

Pierwsze podejście do typowego pseudonimu 1C

Zazwyczaj programiści 1C wiedzą, że podczas nagrywania może zostać zgłoszony wyjątek. Boją się też wyjątków, dlatego starają się złapać je wszystkie. Na przykład tak:


Procedura Bardzo przydatny i ważny kod (Lista łączy do katalogów) StartTransaction(); Dla każdego łącza z listy łączy katalogowych Zapętl obiekt katalogu = Link.GetObject(); Directory Object.SomeField = "Zmieniono mi kod programu"; AttemptDirectoryObject.Write(); Dziennik wyjątków.Error("Nie można zapisać elementu %1", Link); Kontynuować; Zakończ próbę; Koniec cyklu; Zatwierdź transakcję(); Koniec procedury

Cóż, sytuacja się poprawiła, prawda? Przecież teraz możliwe błędy zapisy są przetwarzane, a nawet rejestrowane. Wyjątki nie będą już zgłaszane podczas zapisywania obiektu. A w logu widać nawet na jakim obiekcie, nie byłem zbyt leniwy i umieściłem link w wiadomości zamiast lakonicznego „Błąd podczas pisania katalogu”, jak często lubią pisać programiści, którzy zawsze się spieszą. Inaczej mówiąc, chodzi o troskę o użytkownika i wzrost kompetencji.


Jednak doświadczony użytkownik 1C powie, że nie, nie było lepiej. Właściwie nic się nie zmieniło, a może nawet pogorszyło. W metodzie „Write()” sama platforma 1C rozpocznie transakcję zapisu, a transakcja ta będzie już zagnieżdżona w stosunku do naszej. A jeśli podczas pracy z bazą danych 1C transakcja zostanie wycofana (na przykład zostanie zgłoszony wyjątek logiki biznesowej), wówczas nasza transakcja najwyższego poziomu nadal będzie oznaczona jako „uszkodzona” i nie będzie można jej zatwierdzić. W rezultacie ten kod pozostanie problematyczny, a przy próbie zatwierdzenia wyświetli się komunikat „Wystąpiły już błędy”.


A teraz wyobraźcie sobie, że nie mówimy o małej metodzie, a o głębokim stosie wywołań, gdzie na samym dole ktoś wziął i „wypuścił” rozpoczętą transakcję ze swojej metody. Procedury najwyższego poziomu mogą nie mieć pojęcia, że ​​ktoś na dole rozpoczął transakcje. W rezultacie cały kod zawodzi z powodu niejasnego błędu, którego w zasadzie nie da się zbadać.


Kod rozpoczynający transakcję jest wymagany do jej zakończenia lub wycofania. Niezależnie od jakichkolwiek wyjątków. Należy sprawdzić każdą gałąź kodu, aby sprawdzić, czy metoda kończy się bez zatwierdzania lub anulowania transakcji.

Metody pracy z transakcjami w 1C

Nie byłoby zbędne przypomnienie, co ogólnie zapewnia nam 1C do pracy z transakcjami. Oto dobrze znane metody:

  • Rozpocznij transakcję()
  • Zatwierdź transakcję()
  • Anuluj transakcję()
  • TransakcjaAktywna()

Pierwsze 3 metody są oczywiste i robią to, co mówią ich nazwy. Ostatnia metoda zwraca True, jeśli licznik transakcji jest większy od zera.


I jest ciekawa funkcja. Metody zakończenia transakcji (Commit i Cancel) zgłaszają wyjątki, jeśli liczba transakcji wynosi zero. Oznacza to, że jeśli wywołasz jeden z nich poza transakcją, wystąpi błąd.


Jak prawidłowo stosować te metody? To bardzo proste: należy zapoznać się z zasadą sformułowaną powyżej:


Jak zastosować się do tej zasady? Spróbujmy:


Już powyżej zrozumieliśmy, że metoda „Zrób coś” jest potencjalnie niebezpieczna. Może zgłosić jakiś wyjątek i transakcja „wyjdzie” z naszej metody. OK, dodajmy możliwą procedurę obsługi wyjątków:


Rozpocznij transakcję(); Spróbuj coś zrobić(); Wyjątek // ale co mam tu napisać? Zakończ próbę; Zatwierdź transakcję();

Świetnie, wychwyciliśmy występujący błąd, ale co powinniśmy z tym zrobić? Napisać wiadomość do dziennika? No, może gdyby kod logowania błędów miał być dokładnie na tym poziomie i tu czekamy na błąd. A jeśli nie? A gdybyśmy nie spodziewali się tutaj żadnych błędów? W takim razie powinniśmy po prostu pominąć ten wyjątek i pozwolić, aby zajęła się nim inna warstwa architektury. Odbywa się to za pomocą operatora „CauseException” bez argumentów. W tych twoich JavaScriptach robi się to dokładnie w ten sam sposób, co operator rzutu.


Rozpocznij transakcję(); Spróbuj coś zrobić(); Wyjątek ZgłoszenieWyjątku; Zakończ próbę; Zatwierdź transakcję();

Więc czekaj... Jeśli po prostu rzucimy wyjątek dalej, to dlaczego w ogóle potrzebna jest próba? Oto dlaczego: reguła zmusza nas do zapewnienia zakończenia rozpoczętej transakcji.


Rozpocznij transakcję(); Spróbuj coś zrobić(); WyjątekAnulujTransakcję(); rzutWyjątek; Zakończ próbę; Zatwierdź transakcję();

Teraz wydaje się, że jest pięknie. Pamiętamy jednak, że nie ufamy kodowi Do Something(). Co jeśli autor w środku nie przeczytał tego artykułu i nie wie, jak pracować z transakcjami? A co jeśli tam to zabierze i wywoła metodę CancelTransaction lub wręcz przeciwnie, dopuści się tego? Jest to dla nas bardzo ważne procedura obsługi wyjątków nie zgłosiła nowego wyjątku, w przeciwnym razie pierwotny błąd zostanie utracony, a rozwiązywanie problemów stanie się niemożliwe. Pamiętamy, że metody Commit i Cancel mogą zgłosić wyjątek, jeśli transakcja nie istnieje. Tutaj z pomocą przychodzi metoda TransactionActive.

Wersja ostateczna

Wreszcie możemy napisać poprawną, „bezpieczną dla transakcji” wersję kodu. Tutaj jest:


**UPD: komentarze sugerowały bezpieczniejszą opcję, gdy CommitTransaction znajduje się w bloku Attempt. Ta konkretna opcja jest pokazana tutaj; poprzednio naprawa znajdowała się po bloku Próba-Wyjątek.


Rozpocznij transakcję(); Spróbuj coś zrobić(); Zatwierdź transakcję(); Wyjątek, jeśli TransactionIsActive() Następnie CancelTransaction(); koniecJeśli; rzutWyjątek; Zakończ próbę;

Poczekaj, ale nie tylko „Anuluj transakcję” może powodować błędy. Dlaczego więc „CommitTransaction” nie jest opakowany w tym samym stanie co „TransactionActive”? Ponownie, stosując tę ​​samą zasadę: Za jej zakończenie powinien odpowiadać kod, który rozpoczął transakcję. Nasza transakcja niekoniecznie jest pierwszą, można ją zagnieżdżać. Na naszym poziomie abstrakcji musimy jedynie dbać o naszą transakcję. Wszystkie inne nie powinny nas interesować. To obcy ludzie, nie powinniśmy za nich odpowiadać. Właśnie NIE POWINNI. Nie należy podejmować prób ustalenia rzeczywistego poziomu licznika transakcji. To ponownie przerwie enkapsulację i doprowadzi do „rozmazania” logiki zarządzania transakcjami. Sprawdziliśmy tylko aktywność w procedurze obsługi wyjątków i tylko po to, aby upewnić się, że nasza procedura obsługi nie wygeneruje nowego wyjątku, „ukrywając” stary.

Lista kontrolna refaktoryzacji

Przyjrzyjmy się niektórym z najczęstszych sytuacji wymagających interwencji kodu.


Wzór:


Rozpocznij transakcję(); Zrób coś(); Zatwierdź transakcję();

Owiń go w „bezpieczny” projekt za pomocą Próby, Utrzymuj przy życiu i Zgłoś wyjątek.


Wzór:


Jeśli NotTransactionActive() thenStartTransaction()EndIf

Analiza i refaktoryzacja. Autor nie rozumiał co robi. Można bezpiecznie rozpocząć transakcje zagnieżdżone. Nie ma potrzeby sprawdzania warunku, wystarczy rozpocząć zagnieżdżoną transakcję. Poniżej w module prawdopodobnie jest tam nadal zniekształcony ich utrwaleniem. To gwarantowane hemoroidy.


Mniej więcej podobna opcja:


Jeśli transakcja jest aktywna(), wówczas CommitTransaction() EndIf

podobnie: dokonywanie transakcji według warunku jest dziwne. Dlaczego jest tu taki warunek? Co, ktoś inny mógł już odnotować tę transakcję? Powód rozprawy.


Wzór:


StartTransaction() While Select.Next() Loop // czytanie obiektu przez referencję // zapisywanie obiektu EndCycle; Zatwierdź transakcję();
  1. wprowadzić kontrolowane blokowanie, aby uniknąć zakleszczenia
  2. wprowadź wywołanie metody Block
  3. zawiń „try”, jak pokazano powyżej

Wzór:


StartTransaction() Podczas próby pętli Select.Next() Object.Write(); Raport o wyjątkach („Zapis nie powiódł się”); Zakończ próbę; Koniec cyklu; Zatwierdź transakcję();

W przypadku wyjątku ta transakcja nie zostanie już ukończona. Nie ma sensu kontynuować cyklu. Kod należy przepisać, sprawdzając oryginalne zadanie. Dodatkowo podaj bardziej informacyjny komunikat o błędzie.

Wreszcie

Ja, jak już zapewne się domyślacie, należę do osób, które uwielbiają platformę 1C i rozwój na niej. Oczywiście są skargi na platformę, szczególnie w środowisku Highload, ale ogólnie pozwala ona niedrogo i szybko tworzyć aplikacje korporacyjne o bardzo wysokiej jakości. Dostarczanie gotowego do użycia ORM-u, GUI, interfejsu sieciowego, raportowania i wielu innych funkcji. W komentarzach na temat Habré zwykle piszą różne aroganckie rzeczy, więc chłopaki – głównym problemem 1C jako ekosystemu nie jest platforma ani sprzedawca. To zbyt niski próg wejścia, który pozwala na wejście do branży ludziom, którzy nie rozumieją, czym jest komputer, baza danych, klient-serwer, sieć i tak dalej. 1C sprawił, że tworzenie aplikacji dla przedsiębiorstw stało się zbyt łatwe. W 20 minut napiszę system księgowy zakupów/sprzedaży z elastycznymi raportami i klientem webowym. Po tym łatwo mi pomyśleć, że na większą skalę można pisać w podobny sposób. Jakoś 1C zrobi wszystko wewnętrznie, nie wiem jak, ale prawdopodobnie to zrobi. Napiszę „StartTransaction()”....

Dodaj tagi