Jak zaprojektować magazyn danych AppEngine dla serwisu społecznościowego, takiego jak Twitter?

Zastanawiam się, jaki byłby najlepszy sposób na zaprojektowanie aplikacji społecznościowej, w której członkowie wykonują działania i śledzą działania innych użytkowników za pomocą Google AppEngine.

Aby być bardziej szczegółowym Załóżmy, że mamy te byty:

  • użytkownicy którzy mają znajomych
  • Activities które reprezentują akcje wykonane przez użytkowników (powiedzmy, że każdy ma wiadomość tekstową i odniesienie do swojego właściciela użytkownika, lub może użyć skojarzenia nadrzędnego poprzez appengine ' s klucz)

Najtrudniejszą częścią jest śledzenie aktywności znajomych, co oznacza agregowanie najnowszych aktywności od wszystkich znajomych. Normalnie byłoby to połączenie między tabelą działań a listą znajomych, ale nie jest to realny projekt na appengine, ponieważ nie ma symulacji join, która będzie wymagała uruchomienia N zapytań (gdzie N to liczba znajomych), a następnie scalenia w pamięci - bardzo kosztowne i prawdopodobnie przekroczy termin żądania...)

Obecnie myślę o zaimplementowanie tego za pomocą kolejek skrzynek odbiorczych, w których utworzenie nowej aktywności uruchomi proces w tle, który umieści klucz nowej aktywności w "skrzynce odbiorczej"każdego następnego użytkownika:

  • uzyskanie "wszystkich użytkowników, którzy podążają za X" jest możliwym zapytaniem appengine
  • niezbyt drogie wprowadzanie wsadowe do nowej jednostki "skrzynki odbiorczej", która zasadniczo przechowuje krotki (użytkownik, klucz aktywności).

Z przyjemnością wysłucham myśli na temat tego projektu lub alternatywnych sugestii itp.

Author: Dan McGrath, 2009-10-27

4 answers

[[2]}Spójrz na tworzenie skalowalnych, złożonych aplikacji na silniku aplikacji (pdf ), fascynujący wykład wygłoszony na Google I / O przez Bretta Slatkina. Zajmuje się problemem budowania skalowalnej usługi przesyłania wiadomości, takiej jak Twitter.

Oto jego rozwiązanie używając właściwości list:

class Message(db.Model):
    sender = db.StringProperty()
    body = db.TextProperty()

class MessageIndex(db.Model):
    #parent = a message
    receivers = db.StringListProperty()

indexes = MessageIndex.all(keys_only = True).filter('receivers = ', user_id)
keys = [k.parent() for k in indexes)
messages = db.get(keys)

Ten klucz tylko zapytanie znajduje indeksy wiadomości z odbiornikiem równym temu, który podałeś, bez deserializacji i serializacji listy odbiorników. Następnie używasz tych indeksów do zbieraj tylko te wiadomości, które chcesz.

Oto niewłaściwy sposób aby to zrobić:

class Message(db.Model):
    sender = db.StringProperty()
    receivers = db.StringListProperty()
    body = db.TextProperty()

messages = Message.all().filter('receivers =', user_id)

Jest to nieefektywne, ponieważ zapytania muszą rozpakować wszystkie wyniki zwrócone przez zapytanie. Jeśli więc zwróciłeś 100 wiadomości z 1000 użytkownikami na każdej liście odbiorców, będziesz musiał deserializować wartości właściwości listy 100 000 (100 x 1000). Zbyt drogie opóźnienia magazynów danych i procesora.

Na początku byłem trochę zdezorientowany tym wszystkim, więc napisałem krótki tutorial o korzystanie z właściwości list . Enjoy :)

 24
Author: wings,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-27 18:25:34

Nie wiem, czy jest to najlepszy projekt dla aplikacji społecznościowej, alejaiku zostałprzeportowany na silnik aplikacji przez jej oryginalnego twórcę, gdy firma została przejęta przez Google, więc powinno być rozsądne.

Zobacz dział aktorzy i tygrysy i niedźwiedzie, o mój Boże! in design_funument.txt . Encje są zdefiniowane w common/models.py A zapytania są w common/api.py .

 7
Author: user103576,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-27 19:58:05

Myślę, że można to teraz rozwiązać za pomocą nowych zapytań projekcyjnych w NDB.

class Message(ndb.Model):
    sender = ndb.StringProperty()
    receivers = ndb.StringProperty(repeated=True)
    body = ndb.TextProperty()

messages = Message.query(Message.receivers == user_id).fetch(projection=[Message.body])

Teraz nie masz do czynienia z drogimi kosztami deserializacji własności listy.

 0
Author: Robert Do,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-17 18:49:18

Robert, o Twoim proponowanym rozwiązaniu:

messages = Message.query(Message.receivers == user_id).fetch(projection=[Message.body])
Chyba ndb.TextProperty "body" nie może być używany z projekcjami, ponieważ nie jest indeksowany. Projekcje obsługują tylko właściwości indeksowane. Poprawnym rozwiązaniem byłoby utrzymanie 2 tabel: Message I MessageIndex.
 0
Author: HarlockBcn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-23 18:49:55