2012-09-05 14 views
5

Moja konfiguracja:node.js + Mongo + Aktualizacja atomowej z wieloma podmiotami = ból głowy

  1. node.js
  2. Mongojs
  3. Prosta baza danych zawierająca dwie kolekcje - magazyn i faktur.
  4. Użytkownicy mogą jednocześnie tworzyć faktury.
  5. Faktura może odnosić się do kilku pozycji w ekwipunku.

Mój problem:

Utrzymanie integralności inwentaryzacji. Wyobraźmy sobie scenariusz, w którym dwóch użytkowników przesłało dwie faktury z nakładającymi się zestawami przedmiotów.

Naiwny (i źle) realizacja będzie wykonać następujące czynności:

  1. Dla każdej pozycji na fakturze przeczytać odpowiednią pozycję z kolekcji zapasów.
  2. Napraw liczbę przedmiotów z ekwipunku.
  3. Jeśli jakakolwiek ilość sztuk spadnie poniżej zera - zrezygnuj z żądania z odpowiednią wiadomością dla użytkownika.
  4. Zapisz pozycje w ekwipunku.
  5. Zapisz fakturę.

Oczywiście ta implementacja jest zła, ponieważ działania obu użytkowników będą się wzajemnie oddziaływać i wpływać na siebie nawzajem. W typowym serwerze blokującym + relacyjnej bazie danych jest to rozwiązywane za pomocą złożonych schematów blokowania/transakcji.

Co to jest za monodramowy sposób rozwiązania tego problemu? Czy są jakieś narzędzia, które platforma node.js zapewnia tego typu rzeczy?

+0

Pan spojrzał na MongoDB-sugerowanej dwufazowe podejście opisane [tutaj] (http://cookbook.mongodb.org/patterns/perform-two-phase-commits/)? – JohnnyHK

Odpowiedz

5

Możesz spojrzeć na metodę dwufazowego zatwierdzania z MongoDB, lub możesz całkowicie zapomnieć o transakcjach i oddzielić swoje procesy za pomocą podejścia magistrali usług. Przykładem może być Amazon - pozwolą na przesłanie zamówienia, ale nie potwierdzą go, dopóki nie będą w stanie zabezpieczyć swojego ekwipunku, nie obciążą karty itp. Żadna z tych sytuacji nie występuje w pojedynczej transakcji - jest to szereg kroków, które mogą występować oddzielnie i mogą w razie potrzeby mieć zastosowane kroki kompensacyjne.

Naiwny realizacja autobus wykonaj następujące czynności (należy pamiętać, że jest to tylko ogólna sugestia, aby pracować i dokładna realizacja będzie zależeć od konkretnych potrzeb dla współbieżności, etc.):

  1. połóż zamówienie w kolejce. W tym momencie możesz nadal czekać na klienta, lub możesz podziękować im za zamówienie i poinformować ich, że otrzymają wiadomość e-mail po przetworzeniu jej przez .
  2. "Pracownik magazynu" złapie zamówienie i zablokuje inwentarz przedmiotów, które musi zarezerwować. Można to zrobić na wiele różnych sposobów: . Za pomocą Mongo możesz utworzyć kolekcję, która ma dokument dla każdego zamówienia. Ten dokument będzie miał identyfikator ID elementu magazynu i TTL, który jest rozsądny (powiedzmy 30 sekund).Dopóki robotnik ma zamek, może on zarządzać poziomami zapasów przedmiotów, dla których ma blokady. Po wprowadzeniu zmian przez użytkownika może on usunąć dokument "zablokowany".
  3. Jeśli pojawi się inny pracownik, który chce zarządzać tym samym przedmiotem , gdy jest zablokowany, można przełączyć zablokowanego pracownika w tryb uśpienia na przez X sekund, a następnie ponowić próbę lub, jeszcze lepiej, ponownie złożyć wniosek na autobus komunikacyjny zostanie odebrany później przez innego pracownika .
  4. Gdy pracownik został rozwiązany wszystkich pozycji zapasów, to wtedy można miejsce kolejny komunikat na magistrali usługowej, która wskazuje kartę powinna być naładowana, czy przetwarzanie powinno otrzymać powiadomienie ciągnąć wykazie lub e-mail może być wysłane do osoby, która wykonała zamówienie itp., itp.,

Brzmi skomplikowanie, ale gdy już skonfigurujesz magistralę komunikatów, jest ona względnie prosta. A list of Node Message Bus Implementations can be found here.

Niektórzy deweloperzy całkowicie pomijają formalną magistralę komunikatów i używają bazy danych jako silnika przekazującego wiadomości, który może pracować w prostych implementacjach. Google Mongo i Queues.

Jeśli nie spodziewasz się więcej niż jednego serwera, a implementacja magistrali komunikatów jest zbyt duża, węzeł może obsłużyć blokowanie i przekazywanie wiadomości. Na przykład, jeśli naprawdę chcesz zablokować za pomocą węzła, możesz utworzyć tablicę przechowującą identyfikatory elementów magazynu. Chociaż, szczerze mówiąc, myślę, że autobus z wiadomościami to najlepsza droga. W każdym razie, oto kod, którego używałem w przeszłości do obsługi prostego zewnętrznego blokowania zasobów za pomocą Node.

// attempt to take out a lock, if the lock exists, then place the callback into the array. 
this.getLock = function(id, cb) { 

     if(locks[id]) { 
      locks[id].push(cb); 
      return false; 
     } 
     else { 
      locks[id] = []; 
      return true; 
     } 
    }; 

// call freelock when done 
this.freeLock = function(that, id) { 
      async.forEach(locks[id], function(item, callback) { 
       item.apply(that,[id]); 
       callback(); 
      }, function(err){ 
       if(err) { 
        // do something on error 
       } 

       locks[id] = null; 

      }); 
     }; 
+0

Dziękuję bardzo, wygląda świetnie. – mark

+0

Jest coś, czego nie rozumiem - co można zrobić, jeśli w twoim przykładzie wystąpi błąd? Część, w której znajduje się komentarz "zrób coś na temat błędu". Chcę teraz trzymać się najprostszej rzeczy - pojedynczy serwer i żądanie zwracają się z wynikiem, dobrym lub złym, ale bez długotrwałych zadań. Serwer jest procesem z jednym węzłem, więc co masz na myśli przez pracownika magazynu? – mark

Powiązane problemy