Dapper - insert danych powoduje deadlock

Dapper - insert danych powoduje deadlock
AdamWox
  • Rejestracja:ponad 7 lat
  • Ostatnio:około godziny
  • Lokalizacja:Jastrzębie-Zdrój
  • Postów:2157
0

Witam.
Mam panel w Angularze + ASP .NET Core 3.0 za pomocą którego dodaje się dokumenty. Mam możliwość dodawania dwóch typów dokumentów RO (Rezerwacja odbiorcy) oraz FPF (Faktura Pro Forma). W przypadku dodawania FPF, problemu nie ma, wszystkie pozycje są poprawnie dodane, ale w przypadku RO dostaje deadlocki... Metoda dodawania dokumentów ta sama.

Czy może mi ktoś wyjaśnić jakim cudem insert danych dapperem powoduje deadlock? Czy jestem w stanie coś z tym zrobić, jakiś timer, thread.sleep? Nie rozumiem dlaczego akurat teraz mam takie problemy.

JU
  • Rejestracja:około 22 lata
  • Ostatnio:około miesiąc
  • Postów:5042
0

Pokaż kod. Może masz jakiś trigger, który to powoduje.

AdamWox
  • Rejestracja:ponad 7 lat
  • Ostatnio:około godziny
  • Lokalizacja:Jastrzębie-Zdrój
  • Postów:2157
0
Kopiuj
        public void SaveTraNag(TraNag trn)
        {
            try
            {
                int id = _sqlService.InsertTraNag(trn).Result;
                for (int i = 0; i < trn.Pozycje.Count; i++)
                {
                    TraElem tre = trn.Pozycje[i];
                    tre.TrE_TrNId = id;
                    _sqlService.InsertTraElem(tre); 
// W tym miejscu dostaje deadlocki, ponieważ mam tylko jedną pozycje na dokumencie, zawsze tylko pierwszą.
                }

                for (int i = 0; i < trn.VAT.Count; i++)
                {
                    TraVAT trv = trn.VAT[i];
                    trv.TrV_TrNid = id;
                    _sqlService.InsertTraVAT(trv);
                }
            }
            catch(Exception ex)
            {
                _loggerManager.Error(ex);
            }
        }
Kopiuj
        public async Task<int> InsertTraElem(TraElem tre)
        {
            using (IDbConnection db = new SqlConnection(CompanyConnectionString()))
            {
                return await db.InsertAsync(tre);
            }
        }
W2
Pytanie na marginesie - to Twój kod czy po kimś odziedziczony i tylko dopisujesz funkcjonalności ?
W2
Ok, to powiem szczerze nie wiem od czego zacząć...
AdamWox
W jakim sensie?
W2
  • Rejestracja:około 19 lat
  • Ostatnio:16 dni
3

Ok kilka wdechów i jedziemy.

Za każdym razem otwierasz asycnhronicznie połączenie do bazy (czemu ?) w InsertTraElem. Nie czakasz na wstawienie tych elementów _sqlService.InsertTraVAT(trv) nie ma awaita (napisałbym że nie ma .Result ale przez palce mi to nie przejdzie) więc problem leży tutaj.

Błagam poczytaj o kolekcjach, asynchroniczności, łapaniu wyjątków, nazwach zmiennych, łączeniu się z bazą. Tutaj ani jedna linijka nie jest zgodna ze sztuka i konwencjami w .NET

Kod jest zły, bardzo zły...

Przemyśl sobie cała strukturę bo to naprawde powinno wyglądac inaczej. Zakładam że wywołujesz to w kontrolerze - jeśli tak to nie widze powodu żeby nie ciagnąc asynychronicznych wywołan aż do kontrolera - .Result to najgrosze co można tu zrobić. Nie otwieraj poaćzenia za każdym razem w pętli for - otwieraj jedno na całą metodę.

edytowany 2x, ostatnio: W2K
AdamWox
  • Rejestracja:ponad 7 lat
  • Ostatnio:około godziny
  • Lokalizacja:Jastrzębie-Zdrój
  • Postów:2157
0

Co mam wyczytać z tych kolekcji, asynchroniczności, łapania wyjątków, nazwach zmiennych, łączeniu z bazą? Czego mi brakuje? Dlaczego nazwy zmiennych dla ciebie są złe? Co jest złego w łapaniu wyjątków i co jest złego z kolekcjami? Ok, z połączeniem do bazy masz rację, ale to proszę o jakąś propozycję, jak mniej więcej to poprawić, co zmienić, gdzie ?

W2
  • Rejestracja:około 19 lat
  • Ostatnio:16 dni
2

Bardzo na szybko:

Kopiuj
public async Task SaveTransaction(TraNag trn)
{
//nie łap wyjatków tutaj skro nic sensownego z nimi nie robisz - logowanie powino byc w moddleware Asp.NET

using(var connection=...)//otwieramy połaczenie
//pomyśl o transakcji bazodanowej bo teraz to operacja na otwartym sercu
{
     int id = await _sqlService.InsertTraNag(trn,connection)
     foreach(var tre in trn.Pozycje)
     {
            tre.TrE_TrNId = id;
            await sqlService.InsertTraElem(tre, connection)
     }
//reszta podobnie
}

}

To co podałem nadal nie jest dobrym kodem - wszytskie elementy powinno się wstawiać w paczkkach po kilka (wsyztskie na raz)
Nazwy zmiennych to już inna bajka - po co takie skróty i czemu po polsku.

edytowany 2x, ostatnio: W2K
Aventus
  • Rejestracja:około 9 lat
  • Ostatnio:ponad 2 lata
  • Lokalizacja:UK
  • Postów:2235
1

Ja jeszcze bym dodał że enigmatyczne nazywanie zmiennych nie jest "cool", serio. To praktyka stosowana 20 lat temu i dziś jeszcze przez niektórych hinduskich programistów. Zmienne powinny mieć czytelne, jasno określające ich rolę nazwy. To samo tyczy się typów. No bo czym jest TraNag dla postronnego czytelnika? Mi nic to nie mówi. Z kontekstu domyślam się że to coś związanego z transakcjami, ale kod w którym trzeba się czegoś domyślać to zły kod. Nie ma naprawdę nic złego w długiej nazwie jeśli dobrze odzwierciedla ona przeznaczenie zmiennej/typu.

trv.TrV_TrNid

To już w ogóle czarna magia. Pomijam podkreślnik w nazwie właściwości...


Na każdy złożony problem istnieje rozwiązanie które jest proste, szybkie i błędne.
AdamWox
Z księżyca wszyscy spadli czy co? Mówiłem na forum, że piszę coś pod Comarch Optima. To nie moje nazwy, tylko Comarchu. Piszę to sam, nikt więcej nie musi wiedzieć czego tyczy się nazwa TraNag
Aventus
A skąd ja ma wiedzieć pod co to piszesz, nie śledzę każdej Twojej wypowiedzi na forum. Poza tym trzymać się dobrych praktyk warto nawet pisząc kod tylko dla siebie. Szczególnie jest miałbyś do tego kodu powrócić za kilka miesięcy. To tylko drobne sugestie, nie ma co się burzyć.
AdamWox
No ale tak się nazywają kolumny w bazie. Dlaczego miałbym je nazywać inaczej?
AdamWox
  • Rejestracja:ponad 7 lat
  • Ostatnio:około godziny
  • Lokalizacja:Jastrzębie-Zdrój
  • Postów:2157
0

Dlaczego zakładasz, że nic sensownego z nim nie robię. Właśnie dzięki temu wyjątkowi wiem, że mam błąd w dodawaniu do bazy i deadlocki. Mój middleware rzuca błędem ale to nie jest kwestia tego wątku i ten błąd nic mi nie mówił. Klient zgłasza, że podczas testów dodaje się tylko jedna pozycja do dokumentu więc drążę temat. Nie mam zamiaru tracić czasu i poprawiać go tylko po to, aby poprawnie wrzucić na forum.

Jedni twierdzą, że wszystko co związane z SQL ma być w osobnym serwisie, ty twierdzisz, że obiekt połączenia mam tworzyć w poszczególnych serwisach i w parametrach go przekazywać. Każdy piszę inaczej, a ja trace czas i się próbuje do "każdego" dostosować. Problem jest taki, że mam deadlocki, a nie "brzydki" kod.

W2
  • Rejestracja:około 19 lat
  • Ostatnio:16 dni
0

Problem jest taki, że mam deadlocki, a nie "brzydki" kod.

To nie jest brzydki kod, to jest zły kod, który powoduje deadlocki.
A co robisz z tym wyjątkiem ? W jakis sposób go obsługujesz, próbujesz załagodzić jego skutki ?

Czekaj czekaj ? To jest kod produkcyyjny ? Ktoś tego używa ?

edytowany 2x, ostatnio: W2K
AdamWox
  • Rejestracja:ponad 7 lat
  • Ostatnio:około godziny
  • Lokalizacja:Jastrzębie-Zdrój
  • Postów:2157
0

Tak, to jest kod produkcyjny, ktoś tego używa. Teraz właśnie próbuję coś zrobić z tym co zwrócił mi wyjątek

W2
  • Rejestracja:około 19 lat
  • Ostatnio:16 dni
5

Ok, dyskusja nie ma sensu. Odpowiedź uzyskałes. Deadlocki wynikają z całkiwicie błednej obsługi asynchroniczności, która wynika z fatalnej strukury kodu. Jesli chcesz to naprawic na już (dopchąnac butem i zakleić taśmą) to linijka 10 i 18 powinny na koncu mieć .Result.

Powodzenia Tobie i (przede wszystkim) klientowi.

edytowany 1x, ostatnio: W2K
SZ
To ja zapytam tak na boku, z czego wynikał ten deadlock?. Jak wywołujemy _sqlService.InsertTraElem(tre); bez await'a to wiele wątków próbuje się dostać do tej samej instancji _sqlService? Czy można byłoby się zabezpieczyć np lockiem w tej sytuacji? (pomijając sensowność takiego rozwiązania) Jakbym pisał kod to też tak jak go pokazałeś ale i tak zagadnienie wielowątkowości najczęściej kończą się u mnie na singletonach z lockiem.
W2
Tu niekoniecznie otwiera się wiele watków - może ale nie musi (kwestia implememntacji pod spodem). Otwiera się wiele połączen (każde po kolei) ale na tyle szybko w stosunku do czasu wykonania i odpowiedzi z bazy że są one prawie jak równoległe. Wszytstkie odnosza się do tej samej tabeli - to zawsze rodzi problemy. Z racji tego że to nie muszą (ale mogą) byc osobne watki - lock nic tu nie da.
somekind
Deadlock nie polega na wielu wątkach, tylko na dwóch wątkach wzajemnie na siebie czekających.
YA
@somekind: w ogólności nie muszą być dwa wątki (co nie zmienia faktu, że jest to najprostsza sytuacja ilustrująca deadlock), a wiele wątków czekających wzajemnie na siebie. Obrazowo: http://www.cs.fsu.edu/~baker/cop5611.S03/graphics/F6-1.jpg
somekind
Tak, mój skrót myślowy, ale chodziło mi ogólnie o to, że deadlock bierze się z wzajemnego czekania, a nie z dostępu wielu wątków do jednego zasobu.
AdamWox
  • Rejestracja:ponad 7 lat
  • Ostatnio:około godziny
  • Lokalizacja:Jastrzębie-Zdrój
  • Postów:2157
0

Ja się nie zamykam. Daj jakieś przykłady z gita jak to powinno wyglądać. Jeśli robię coś źle to to chce poprawić. Ale pisanie mi tekstu typu "poczytaj sobie" na mnie nie działa, ponieważ czytanie mnie niczego nie uczy, a już tym bardziej dokumentacji MS.

Chce pisać dobrze, ale niekoniecznie trzeba mnie w ten sposób dissowac.

Sugerując się postami jakie tutaj wpadają od ludzi, którzy chcą wiedzieć czy dobrze piszą, widzę, że im więcej ludzi tym więcej zdań, więc pytanie moje - kto ma rację?

YA
Rację ma kolega @W2K.
AdamWox
Nie mówię, że nie ma racji. Zwyczajnie, w moim przypadku, czytanie nie pomaga. Potrzebuje zrozumieć wizualnie, na przykładzie.
W2
  • Rejestracja:około 19 lat
  • Ostatnio:16 dni
0

Struktura tego fragmentu duzo mówi na temat organizacji całej aplikacji. Żeby tutaj cos sensownie poprawić musiałbyś pokazać cały kod - całe repo. To nje jest jeden fix w jednym punkcie.
1 . Asynchronicznośc to jest njapoważniejszy problem (BTW? skąd w ogoóle w tej metodzie wział ci sie async ? ).
2. Jesli to ASp.NET to połaczenie powinno być jedno per request - wstrzykiwane do serwisu z poziomu middleware. To nie wyklucza zmaknięcia całego SQLa w serwisie - jesli zrobisz poprawne Dependency Injection
3. Serwis nie powinien wywoływać innych serwisów
4. Kolekcje - tu naprawde musisz poczytać - to w jaki sposób dodajesz elementy to metody sprzed 20 lat, po to jest IEnumerable, Linq, foreach żeby nie iterować po elementach tablicy.
5. Wyjatki - tak jak pisałem wczesniej - gdybyś w catchu próbował jakoś zareagować na sytuację - odtworzyc połączenie zmienić dane to spoko miałoby to sens ale zalogowanie błędu nie jest jego obsłużeniem - powinien poleciec wyżej do middlewara i pokzać się userowi.

WL
  • Rejestracja:około 21 lat
  • Ostatnio:około 2 miesiące
  • Postów:1082
0

To zakleszczenie jest na poziomie MSSQL czy współbieżności w kodzie .NET?
Bo myślałem, że na poziomie MSSQL, ale teraz to już nie jestem pewien...

Bo jeśli jest na poziomie MSSQL, to... chyba wszyscy piszący mają blade pojęcie o MSSQL, czym jest deadlock w MSSQL, jak go łapać i jak się przed nim chronić.
To jak to jest @AdamWox ?

Bo ten komentarz @W2K w kontekście bazy danych MS SQL (tylko i wyłącznie kontekście bazy danych):

"Otwiera się wiele połączen (każde po kolei) ale na tyle szybko w stosunku do czasu wykonania i odpowiedzi z bazy że są one prawie jak równoległe."

To nie jest ten problem.
Pewnie, że ilość połączeń do bazy powinna być jak najmniejsza a transakcje bazodanowe jak najkrótsze, ale to zupełnie inny obszar.

" Wszytstkie odnosza się do tej samej tabeli - to zawsze rodzi problemy."

A to z kolei jest po prostu nieprawdą, jeśli w tym zdaniu "tabela " oznacza tabelę w bazie danych.
Jak wam się wydaje, skoro to prawda to jakim cudem bazy danych są wa stanie obsłużyć wielu użytkowników, którzy dodają/czytają dane z tych samych tabel?
Czy to rodzi problemy? Właśnie...

Samo dodanie wiersza do tej samej tabeli z wielu wątków, gdzie każdy z nich ma własne połączenie do bazy danych, nie będzie powodował problemów. Nigdy, jeśli zrobi się to zgodnie ze sztuką.
Ze współbieżnością w bazach danych (tak ogólnie) jest jedna złota zasada: jeden wątek = jedno połączenie do bazy danych.
Wątki nie mogą współdzielić tego samego połączenia do bazy danych w tym samym czasie w celu wykonywania operacji na bazie danych.

AdamWox
  • Rejestracja:ponad 7 lat
  • Ostatnio:około godziny
  • Lokalizacja:Jastrzębie-Zdrój
  • Postów:2157
0

Rozwiązanie jakie zaproponował "na szybko" @W2K pomogło. W tym momencie dokumenty dodają się poprawnie. W dalszym ciągu nie wiem na czym polega problem, ponieważ dokumenty innego typu dodawały się poprawnie bez tej poprawki. Baza nie jest moja, to Comarch Optima, tam jest mnóstwo triggerów i "głupot" porobionych przez samego producenta.

Bo jeśli jest na poziomie MSSQL, to... chyba wszyscy piszący mają blade pojęcie o MSSQL, czym jest deadlock w MSSQL, jak go łapać i jak się przed nim chronić.
To jak to jest @AdamWox ?

Ty mi powiedz, bo ja, serio, nie mam zielonego pojęcia, a już tym bardziej jak wkroczyłeś do akcji. Czyli co @W2K ma rację, ponieważ zaczęło mi to działać, ale nie ma racji bo wyszło fartem?

WL
  • Rejestracja:około 21 lat
  • Ostatnio:około 2 miesiące
  • Postów:1082
0
AdamWox napisał(a):

Rozwiązanie jakie zaproponował "na szybko" @W2K pomogło. W tym momencie dokumenty dodają się poprawnie. W dalszym ciągu nie wiem na czym polega problem, ponieważ dokumenty innego typu dodawały się poprawnie bez tej poprawki. Baza nie jest moja, to Comarch Optima, tam jest mnóstwo triggerów i "głupot" porobionych przez samego producenta.

Tak, miałem wątpliwą przyjemność popatrzenia sobie z bardzo bliska na Comarch ERP XL przy niebanalnym obciążeniu.
Za to co zrobił producent, ale przede wszystkim wysoko opłacani konsultanci od siedmiu boleści nadaje się tylko wpierdol w dyby i do lochu.
Ci ludzie nie mają większego pojęcia o tym co tak naprawdę robią - związać drutem, dopchnąć kolanem i dalej, następny do golenia.
A i przy okazji wrogów sobie narobiłem, ponieważ na trójstronnym spotkaniu szef działu R&D dużego partnera Comarch stwierdził, że "zmiana connection string wymaga rekompilacji ich dodatku do Comarch ERP XL i będzie to kosztowało jakieś kilka tyś.".
I w tym momencie nie opanowałem odruchów i parsknąłem opluwając się kawą...

Bo jeśli jest na poziomie MSSQL, to... chyba wszyscy piszący mają blade pojęcie o MSSQL, czym jest deadlock w MSSQL, jak go łapać i jak się przed nim chronić.
To jak to jest @AdamWox ?

Ty mi powiedz, bo ja, serio, nie mam zielonego pojęcia, a już tym bardziej jak wkroczyłeś do akcji.

Zaraz, chwila...
Nie wiesz jaki masz wyjątek i co go wywołało?
Napisałeś deadlock w kontekście bazy danych.

Ja to rozumiem jako zakleszczenie po stronie MSSQL.
MSSQL w przypadku zakleszczenia zwraca odpowiedni wyjątek, o taki:
Transaction (Process ID XYZ) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

To gdzie masz zakleszczenie, w bazie danych czy w aplikacji?

Czyli co @W2K ma rację, ponieważ zaczęło mi to działać, ale nie ma racji bo wyszło fartem?

Może tak być, ale to nie jest takie oczywiste.
Zakleszczenia w aplikacji i w bazie danych bywają dość wredne i trudne do ubicia.
To co opisał @W2K ma zastosowanie przede wszystkim do kodu aplikacji i, tak naprawdę, nie wychodzi poza obszar dobrych praktyk. A jeśli się mylę, to pewnie @W2K mnie poprawi a ja się chętnie czegoś nauczę. Nie ukrywam, że .NET to nie moje "środowisko naturalne".

Ja pisałem tylko i wyłącznie o bazie danych.

Jedno z drugim może być powiązane, ale nie musi.
Zatem skąd przyszedł wyjątek?

edytowany 1x, ostatnio: wloochacz
W2
  • Rejestracja:około 19 lat
  • Ostatnio:16 dni
0

W skrócie : ten kod jest tak zły że aż ciekawy jako badanie zachowania bazy i połączenia :-D

Kluczowe żeby wyjaśnić co tak naprawdę się stało to konkretny wyjątek który otrzymujesz.
@wloochacz tak - zgadzam się z Tobą że bazy są zaprojektowane do tego żeby otwierać dziesiątki - setki połączeń jednocześnie i wykonywać te same operacjie. Też mnie to mocno zastanowiło. Stąd moje nieprecyzyjne określenie - to zawsze rodzi problemy. Nie jestrem pewny co dokładnie dzieje się na bazie w takiej syuacji, ale pewnym jest że otworzenie kilkudziesięciu połączeń w pętli każde tylko po to żeby wstawić jeden rekord musi skończyc się źle. Zresztą mamy bazę działającą pewnie na jakimś niespecjalnie wydajnym serwerze, a mówimy to o odpaleniu kilki do kilkudziesięciu połączeń w przedziale poniżej 1ms. Problem z pewnością leży w równoległym otwieraniu tych połączeń (nie jest wiadome czy każde z nich to osobny wątek) bo dodanie Result - czyli wymuszenie synchroniczności naprawiło błąd. I podkreślam w dalszym ciągu jest to rozwiązanie złe- nawet bardzo złe nawet po łatce którą zaproponowałem.
Sam chętnie poznam treść wyjątku jaki leci bo żeby było ciekawiej insert masz w usingu. co w ogóle przenoosi problem w jeszcze ciekawsze obszary. Istnieje szansa że połączenie ulega zamknięciu zanim się wykona całkowicie.

edytowany 2x, ostatnio: W2K
somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:około 5 godzin
  • Lokalizacja:Wrocław
1
AdamWox napisał(a):

Rozwiązanie jakie zaproponował "na szybko" @W2K pomogło. W tym momencie dokumenty dodają się poprawnie. W dalszym ciągu nie wiem na czym polega problem, ponieważ dokumenty innego typu dodawały się poprawnie bez tej poprawki.

Może miałeś szczęście, takie rzeczy się zdarzają.

Problem polegał na tym, że napisałeś kod, który powodował deadlock - tak by design.

  1. Z głównego wątku w metodzie SaveTraNag wywołujesz asynchroniczną metodę InsertTraNag.
  2. W niej masz return await db.InsertAsync(cośtam);, ta linijka rozpoczyna komunikację z bazą i zwraca rozpoczęty Task (wykonywany przez jakiś wątek z puli). Task to nie jest wynik działania metody, który nas docelowo interesuje, to tylko informacja, że coś się dzieje i na to czekamy; obietnica, że działanie za jakiś czas się zakończy.
  3. InsertTraNag czeka (await) na ten Task, czyli przechwytuje kontekst wywołania, aby móc kontynuować po zakończeniu Taska. Nie masz tam co prawda więcej kodu, który by na wyniku Taska działał, ale nadal to działa w ten sposób. Ten niezakończony Task zwracany jest to metody wyżej.
  4. Metoda SaveTraNag z powodu użycia .Result blokuje się na tym Tasku, a więc blokuje w ten sposób wątek kontekstu.
  5. Baza zakończyła pracę, a więc Task zwrócony przez InsertAsync jest zakończony.
  6. Teraz InsertTraNag jest już gotowa do kontynuowania działania, więc czeka na wątek kontekstu, aby mogła iść dalej.
  7. Ale kontekst jest przecież zablokowany, bo synchronicznie czeka na zakończenie InsertTraNag.
  8. No i dupa... znaczy deadlock. Dwie metody w dwóch wątkach czekają na siebie wzajemnie.
wloochacz napisał(a):

Ja to rozumiem jako zakleszczenie po stronie MSSQL.
MSSQL w przypadku zakleszczenia zwraca odpowiedni wyjątek, o taki:
Transaction (Process ID XYZ) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

@wloochacz: Ja na bazach jakoś wybitnie się nie znam, ale zakleszczenie na samych insertach? To jest w ogóle możliwe?

W2K napisał(a):

Nie jestrem pewny co dokładnie dzieje się na bazie w takiej syuacji, ale pewnym jest że otworzenie kilkudziesięciu połączeń w pętli każde tylko po to żeby wstawić jeden rekord musi skończyc się źle. Zresztą mamy bazę działającą pewnie na jakimś niespecjalnie wydajnym serwerze, a mówimy to o odpaleniu kilki do kilkudziesięciu połączeń w przedziale poniżej 1ms. Problem z pewnością leży w równoległym otwieraniu tych połączeń (nie jest wiadome czy każde z nich to osobny wątek) bo dodanie Result - czyli wymuszenie synchroniczności naprawiło błąd.

Dodanie Result, czyli tworzenie synchronicznego wrappera na asynchroniczny kod, to akurat przyczyna błędu. Tworzenie wielu obiektów SqlConnection nie jest dobrą praktyką, ale też nie jest jakimś dużym problemem, bo .NET sam zajmuje się connection poolingiem.

SO
Ale on korzysta z Asp.Net Core, a tam nie ma kontekstu synchronizacji, czyli synchroniczne blokowanie na asynchronicznym kodzie nie powinno samo w sobie powodować deadlocka.
somekind
A to ciekawe. No, ale już i tak napisał, że ma deadlocki na bazie.
WL
  • Rejestracja:około 21 lat
  • Ostatnio:około 2 miesiące
  • Postów:1082
0
wloochacz napisał(a):

Ja to rozumiem jako zakleszczenie po stronie MSSQL.
MSSQL w przypadku zakleszczenia zwraca odpowiedni wyjątek, o taki:
Transaction (Process ID XYZ) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

@wloochacz: Ja na bazach jakoś wybitnie się nie znam, ale zakleszczenie na samych insertach? To jest w ogóle możliwe?

Nie, to nie jest możliwe, jeśli to naprawdę tylko prosty insert into do tabeli.
Ale nie wiem, czy ów DAL (tu mamy pod spodem Dappera) nie robi w trakcie wykonywania komend dodających dokument jakiś selectów.
Poza tym nie wiem czy na owej bazie są jakieś triggery, które mogą robić kuku.
Podłączyłby chłop SQL Profilera, to by się dowiedział, a tak to...

JP
Ta tabela, TraElem, ma trigger na Insert i Update na 1600 linijek :)
YA
  • Rejestracja:prawie 10 lat
  • Ostatnio:około godziny
  • Postów:2368
0

Nie znam się na .net, ale z perspektywy MS SQLa, może chodzić o deadlocki na foreign keys (przykładowy scenariusz: https://www.sqlservergeeks.com/sql-server-foreign-key-deadlocks/)

Z tego kodu (czysta spekulacja) może chodzić o taką sytuację:

  • na osobnym wątku leci insert (wstawia rekord parent z jakimś PK, ale commita transakcji jest gdzieś poza tym kodem, który autor wstawił?)
  • na osobnych połączeniach lecą inserty do tabelki "child", która odwołuje się do tabelki nadrzędnej (via foreign key)

Wątek child czeka, aż parent się zakończy (ale gdzie jest zatwierdzenie transakcji dla parenta?)

edytowany 1x, ostatnio: yarel
AdamWox
  • Rejestracja:ponad 7 lat
  • Ostatnio:około godziny
  • Lokalizacja:Jastrzębie-Zdrój
  • Postów:2157
0

Cały stacktrace wyjątku

Kopiuj
2019-10-29 08:41:12.6166 ERROR System.Data.SqlClient.SqlException (0x80131904): Transaction (Process ID 437) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at System.Data.SqlClient.SqlDataReader.TryHasMoreRows(Boolean& moreRows)
   at System.Data.SqlClient.SqlDataReader.TryReadInternal(Boolean setTimeout, Boolean& more)
   at System.Data.SqlClient.SqlDataReader.<>c__DisplayClass190_0.<ReadAsync>b__1(Task t)
   at System.Data.SqlClient.SqlDataReader.InvokeRetryable[T](Func`2 moreFunc, TaskCompletionSource`1 source, IDisposable objectToDispose)
--- End of stack trace from previous location where exception was thrown ---
   at Dapper.SqlMapper.QueryAsync[T](IDbConnection cnn, Type effectiveType, CommandDefinition command) in C:\projects\dapper\Dapper\SqlMapper.Async.cs:line 437
   at API.Services.SQLService.QueryCompany[T](String query, Object param) in D:\PROJEKTY\VENTS GROUP\API\API\Services\ISQLService.cs:line 182
   at API.Controllers.DataController.GetDokumenty(DateTime dateFrom, DateTime dateTo, String type) in D:\PROJEKTY\VENTS GROUP\API\API\Controllers\DataController.cs:line 309
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at API.Utils.CustomExceptionMiddleware.ExceptionMiddleware.InvokeAsync(HttpContext httpContext) in D:\PROJEKTY\VENTS GROUP\API\API\Utils\CustomExceptionMiddleware\ExceptionMiddleware.cs:line 34
ClientConnectionId:fc740281-f816-4170-b15a-cc0414051d3d
Error Number:1205,State:51,Class:13
YA
  • Rejestracja:prawie 10 lat
  • Ostatnio:około godziny
  • Postów:2368
0

Ciekawsze niż stack trace aplikacyjny, byłoby uzyskanie informacji po stronie serwera bazodanowego. Szybkie google pokazuje, że narzędzia sqlserverowe potrafią produkować rysunki obrazujące deadlock: https://www.red-gate.com/simple-talk/sql/learn-sql-server/how-to-track-down-deadlocks-using-sql-server-2005-profiler/

WL
Oczywiście, że potrafią. Potrafią je też śledzić, to jest tylko kwestia odpowiedniej konfiguracji SQL Server Profiler.
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)