• CloudPouch NEW!
  • Akademia
  • Blog
  • O stronie
  • Home

Prosty Scheduler Serverless ⏰


Amazon Comprehend - detekcja wrażliwych danych
Pracując nad rozwiązaniem zarządzającym kluczami licencyjnymi mojej aplikacji CloudPouch musiałem się zmierzyć z odroczonym w czasie anulowaniem płatnej subskrypcji.

W momencie, gdy użytkownik z jakiegoś powodu zrezygnuje ze swojej subskrypcji, jego klucz licencyjny musi działać aż do końca obecnego okresu rozliczeniowego. Ponieważ przypadek użycia w moim systemie nie jest skomplikowany, postanowiłem go rozwiązać w możliwie prosty sposób, korzystając z dostępnych usług serverless, zgodnie z pryncypiami architektury sterowanej zdarzeniami (Event-Driven Architecture).

Serverless scheduler

Temat implementacji schedulera w serverless przewija się dość często już od lat. Moim zdaniem jest to na tyle powtarzalny problem, że AWS powinien nam dostarczyć zarządzane rozwiązanie. Rozmowy na Slacku AWS Community Builders póki co nie przyniosły żadnych skutków i wciąż jesteśmy zdani na samych siebie.

Na szczęście AWS dostarcza kilka prymitywów z których można skorzystać budując własny scheduler. Do najpopularniejszych można zaliczyć:

  • DynamoDB TTL
  • Cron w EventBridge (kiedyś w CloudWatch)
  • StepFunctions.

Wybór rozwiązania pod problem biznesowy

Wymienione powyżej rozwiązania różnią się znacznie od siebie.

Przede wszystkim oferują różną dokładność działania, czyli przedział czasu między wyznaczonym, a rzeczywistym czasem wywołania akcji. Przykładowo dla DynamoDB TTL jest to nawet maksymalnie 48 godzin. Stosując Cron w EventBridge możemy wywołać funkcję Lambda co minutę. Ogromna różnica, prawda?
To jest najważniejsza, funkcjonalna charakterystyka, gdyż bezpośrednio wpływa na implementację wymagań biznesowych. W wielu przypadkach trudno sobie wyobrazić sytuację, że nasz Biznes akceptuje dwudniową obsuwę w wykonaniu jakiejś akcji dotyczącej klienta czy użytkownika.

Pozostałe charakterystyki, którymi możemy opisać te rozwiązania są również ważne. Dla wielu maksymalna ilość zaharmonogramowanych akcji będzie równie ważna, jak dokładność. Kolejną cechą będzie maksymalny czas odroczenia akcji w przyszłości. No i oczywiście czy akcja jest cykliczna czy jednorazowa.

Idąc dalej nie można zapominać o kosztach działania oraz poziomie skomplikowania rozwiązania, wpływającym bezpośrednio na czas implementacji.

Biorąc te wszystkie charakterystyki pod uwagę, szybko się okazuje, że scheduler schedulerowi nie równy i ostateczne rozwiązanie zależy od wymagań.

Przypadek biznesowy

Narzędzie CloudPouch to aplikacja typu desktop służąca do analizy i optymalizacji kosztów chmury AWS. Jest dostępna w modelu subskrypcyjnym, za małą miesięczną lub roczną opłatą. Każdy klient ma możliwość anulowania swojej subskrypcji w dowolnym momencie. Wtedy jego klucz licencyjny musi działać aż do końca obecnego okresu rozliczeniowego, za który została już pobrana opłata, czyli do czasu t1 przedstawionego na diagramie.

Mechanizm musi działać analogicznie dla rocznych subskrypcji.

Wybór rozwiązania

Biorąc pod uwagę wymagania biznesowe oraz charakterystyki usług AWS, zdecydowałem się wybrać rozwiązanie, które będzie najłatwiejsze do implementacji i obsługi. Był to klasyczny architektoniczny trade-off, gdyż prostota implementacji została uzyskana kosztem dokładności.

Wybór mechanizmu DynamoDB TTL (Time To Live) okazał się być najlepszy, ponieważ:

  • dokładność nie jest dla mnie najważniejsza, w najgorszym wypadku klient otrzyma dwa dni subskrypcji gratis 😉
  • nie potrzebuję cyklicznych wywołań - daną licencję anuluje się tylko raz
  • jest prosty w implementacji - wystarczy sama tabela DynamoDB oraz jej stream
  • jest zgodny Event-Driven Architecture, a dodatkowo AWS zadba za mnie o to, aby wywołać akcję w przyszłości. Innymi słowy nie muszę pisać Lambdy, które będzie cyklicznie sprawdzać czy należy coś anulować
  • pozwala łatwo sprawdzić, które licencje mają zostać anulowane w przyszłości - wystarczy przejrzeć elementy w tabeli DynamoDB
  • jest najtańszy, choć przy mojej skali każde rozwiązanie byłoby darmowe 😉

Implementacja rozwiązania

Rozwiązanie jest bardzo proste i składa się z funkcji Lambda oraz tabeli DynamoDB wraz ze streamem.

W odpowiedzi na anulowanie subskrypcji przez użytkownika wysyłane jest zdarzenie na szynę eventBus w usłudze EventBridge. Przy pomocy reguł to zdarzenie jest przekierowane do funkcji Lambda scheduler (oraz innych komponentów w pełnym rozwiązaniu, co nie jest pokazane na diagramie). Funkcja Lambda scheduler zapisuje w tabeli Scheduling informacji o licencji która ma zostać anulowana. Ten element (“rekord”) ma bardzo prostą strukturę, składa się z informacji pozwalającej jednoznacznie zidentyfikować licencję w tabeli PaidLicenses oraz czasu kiedy ma to nastąpić.

Data anulowania jest zapisana w formacie Unix time pod atrybutem określonym w konfiguracji tabeli DynamoDB. Ja nazwałem ten atrybut ttl, został on zdefiniowany w CloudFormation tworzącym tabelę w linijce 10:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SchedulingTable:
Type: AWS::DynamoDB::Table
Properties:
AttributeDefinitions:
- AttributeName: PK
AttributeType: S
KeySchema:
- AttributeName: PK
KeyType: HASH
TimeToLiveSpecification:
AttributeName: ttl
Enabled: true
BillingMode: PAY_PER_REQUEST
TableName: Scheduling
StreamSpecification:
StreamViewType: OLD_IMAGE

Korzystając z Node.js (JavaScript) należy zwrócić uwagę, aby obliczyć ttl w odpowiedniej jednostce czasu, czyli w sekundach a nie milisekundach. Stąd też dzielenie przez 1000:

1
const ttl = Math.floor(new Date(cancelationTimeString).getTime() / 1000)

 

Zasada działania

Usługa AWS DynamoDB nieustannie monitoruje naszą tablę danych i w momencie, gdy wartość ttl będzie starsza niż obecny czas to skasuje dany element.

Aby całe rozwiązanie miało sens, musimy reagować na zdarzenia kasowania elementów. Robimy to za pomocą streamu, który wywołuje funkcję deactivatePaidLicense. Payload przesłany do tej funkcji zawiera wszystkie dane tego elementu, które były wcześniej przechowywane w tabeli Scheduling, dzięki czemu funkcja wie, którą licencje ma anulować, dokonując odpowiedniej aktualizacji w tabeli PaidLicenses.

Połączenie pomiędzy streamem a funkcją Lambda jest zdefiniowane w pliku serverless.yml:

1
2
3
4
5
6
7
8
9
10
11
12
functions:
deactivatePaidLicense:
handler: src/deactivatePaidLicense/function.handler
description: Deactivate license upon Paddle event
events:
- stream:
type: dynamodb
arn: !GetAtt SchedulingTable.StreamArn
maximumRetryAttempts: 1
batchSize: 1
filterPatterns:
- eventName: [REMOVE]

Zastosowałem tutaj filtr, dzięki któremu funkcja zostanie wywołana tylko w wyniku usunięcia elementu z tabeli. W ten sposób przenosimy logikę z naszego kodu na konfigurację infrastruktury AWS, co jest oczywiście najlepszą praktyką 😃

Tabela Scheduling jest niezależną tabelą, która przechwuje tylko i wyłączne dane dotyczące anulowania licencji. Nie korzystam tutaj z podejścia single-table design, dlatego nie muszę się martwić o inne typy encji.

Działanie w praktyce

Moje pobieżne testy pokazały, że DynamoDB w regionie us-east-1 kasuje elementy z opóźnieniem 10 do 12 minut po zdefiniowanym czasie w atrybucie ttl. Jest to dużo szybciej niż podane powyżej 48 godziny, jednak nadal dla wielu rozwiązań może nie być akceptowalne. Dodatkowo uczulam, że o ile są to typowe czasy to nie mamy żadnej gwarancji, że zawsze takie będą.

Moje obserwacje opóźnień pokrywają się z testami, które przeprowadził Yan Cui.

W praktyce, całe rozwiązanie przy minimalnym nakładzie czasu poświęconego na programowanie realizuje moje wymagania biznesowe w doskonały sposób. I o to chodziło 😃

CloudPouch

Jeśli jesteś ciekaw(a) do czego służy aplikacja CloudPouch, której jestem autorem, zachęcam do skorzystania z bezpłatnego 7-dniowego triala lub zapoznania się z poniższym, krótkim (półtorej minuty) filmikiem demonstracyjnym.




Cześć

Nazywam się Paweł Zubkiewicz i cieszę się, że tu jesteś!
Od ponad 18 lat profesjonalnie tworzę oprogramowanie, a od 2016 roku pasjonuje się Serverless.
Tą stronę stworzyłem z myślą o Tobie i o nas wszystkich, którzy uważają, że trend serverless trwale zmieni sposób tworzenia oprogramowania.
Więcej o tej stronie...

Kategorie

Pobierz bezpłatny PDF

Poradnik 12 Rzeczy o Serverless

Wybrane artykuły