Dapper - insert danych powoduje deadlock

Odpowiedz Nowy wątek
2019-10-29 08:49

Rejestracja: 2 lata temu

Ostatnio: 6 godzin temu

Lokalizacja: Jastrzębie-Zdrój

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.

Pozostało 580 znaków

2019-10-29 10:32

Rejestracja: 16 lat temu

Ostatnio: 14 godzin temu

0

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

Pozostało 580 znaków

2019-10-29 10:36

Rejestracja: 2 lata temu

Ostatnio: 6 godzin temu

Lokalizacja: Jastrzębie-Zdrój

0
        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);
            }
        }
        public async Task<int> InsertTraElem(TraElem tre)
        {
            using (IDbConnection db = new SqlConnection(CompanyConnectionString()))
            {
                return await db.InsertAsync(tre);
            }
        }
Pytanie na marginesie - to Twój kod czy po kimś odziedziczony i tylko dopisujesz funkcjonalności ? - W2K 2019-10-29 11:02
Ok, to powiem szczerze nie wiem od czego zacząć... - W2K 2019-10-29 11:08
W jakim sensie? - AdamWox 2019-10-29 11:14

Pozostało 580 znaków

W2K
2019-10-29 11:14
W2K

Rejestracja: 14 lat temu

Ostatnio: 16 godzin temu

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, 2019-10-29 11:19

Pozostało 580 znaków

2019-10-29 11:23

Rejestracja: 2 lata temu

Ostatnio: 6 godzin temu

Lokalizacja: Jastrzębie-Zdrój

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 ?

Pozostało 580 znaków

W2K
2019-10-29 11:36
W2K

Rejestracja: 14 lat temu

Ostatnio: 16 godzin temu

Bardzo na szybko:

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, 2019-10-29 11:38

Pozostało 580 znaków

2019-10-29 11:42

Rejestracja: 3 lata temu

Ostatnio: 5 godzin temu

Lokalizacja: UK

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.
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 - AdamWox 2019-10-29 11:44
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ć. - Aventus 2019-10-29 11:48
No ale tak się nazywają kolumny w bazie. Dlaczego miałbym je nazywać inaczej? - AdamWox 2019-10-29 11:51

Pozostało 580 znaków

2019-10-29 11:42

Rejestracja: 2 lata temu

Ostatnio: 6 godzin temu

Lokalizacja: Jastrzębie-Zdrój

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.

Pozostało 580 znaków

W2K
2019-10-29 11:47
W2K

Rejestracja: 14 lat temu

Ostatnio: 16 godzin temu

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, 2019-10-29 11:48

Pozostało 580 znaków

2019-10-29 11:50

Rejestracja: 2 lata temu

Ostatnio: 6 godzin temu

Lokalizacja: Jastrzębie-Zdrój

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

Pozostało 580 znaków

W2K
2019-10-29 11:57
W2K

Rejestracja: 14 lat temu

Ostatnio: 16 godzin temu

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, 2019-10-29 11:58
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. - szydlak 2019-10-29 12:23
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. - W2K 2019-10-29 12:43
Deadlock nie polega na wielu wątkach, tylko na dwóch wątkach wzajemnie na siebie czekających. - somekind 2019-10-30 02:35
@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 - yarel 2019-10-30 08:12
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. - somekind 2019-10-30 09:47

Pozostało 580 znaków

Odpowiedz

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0