67
Отказоустойчивая обработка 10 миллионов OAuth токенов на Tarantool Mons Anderson Игорь Латкин

Отказоустойчивая обработка 10M OAuth токенов на Tarantool / Владимир Перепелица, Игорь Латкин (Mail.ru)

  • Upload
    ontico

  • View
    294

  • Download
    3

Embed Size (px)

Citation preview

Отказоустойчиваяобработка 10 миллионовOAuth токенов на TarantoolMons AndersonИгорь Латкин

супероптимальность ипроизводительность

дисковое хранилище - vinyljson документы

2 / 67

Что ещё…

3 / 67

Get yourdata in RAM

Computeclose to data

Enjoy theperformance

4 / 67

О чём мы будем рассказывать?

5 / 67

О чём мы будем рассказывать?Оглянемся назад: что было 3 года назад?

6 / 67

О чём мы будем рассказывать?Оглянемся назад: что было 3 года назад?

Какие проблемы были?

7 / 67

О чём мы будем рассказывать?Оглянемся назад: что было 3 года назад?

Какие проблемы были?

Master ⟷ Master репликация

8 / 67

О чём мы будем рассказывать?Оглянемся назад: что было 3 года назад?

Какие проблемы были?

Master ⟷ Master репликация

В поисках Ra�'а

9 / 67

О чём мы будем рассказывать?Оглянемся назад: что было 3 года назад?

Какие проблемы были?

Master ⟷ Master репликация

В поисках Ra�'а

Объединяем Ra� и шардинг

10 / 67

О чём мы будем рассказывать?Оглянемся назад: что было 3 года назад?

Какие проблемы были?

Master ⟷ Master репликация

В поисках Ra�'а

Объединяем Ra� и шардинг

Оцениваем результат11 / 67

Зачем?Сборщики почты

Вход внешней почтой

Хранение адресных книг

Выдача действующего access_token

12 / 67

Что такое OAuth токен?

{ "token_type" : "bearer", "access_token" : "XXXXXX", "refresh_token" : "YYYYYY", "expires_in" : 3600}

13 / 67

2013 г.первая схема

классика (M-S)

Ref Ref

rw

Master Slave

F

rw

F

rw

ro

rw

replication

ro

14 / 67

Что такое OAuth токен?

{ "token_type" : "bearer", "access_token" : "XXXXXX", "refresh_token" : "YYYYYY", "expires_in" : 3600}

15 / 67

Проблемы?

16 / 67

Проблемы?25% Outage за 15 минут

50% Outage за 30 минут

100% Outage за 1 час

17 / 67

Проблемы?25% Outage за 15 минут

50% Outage за 30 минут

100% Outage за 1 час

2015 г. - 100% CPU (много бизнес-логики и индексов)

Вторичная логика влияет на основную задачу18 / 67

1.6 +

Новый Tarantool

Master-Masterreplica�on

19 / 67

3 датацентра3 копии

MM - репликация

20 / 67

Ref

Ref

M

M

M

Ref

M100

Datapro

Dataline

21 / 67

Ref

Ref

M

M

M

Ref

M100

Datapro

Dataline

в 3 разабольше

запросовнуженвыборлидера

22 / 67

Paxos ?

23 / 67

Paxos ?

24 / 67

Ra�!

25 / 67

M

M

M

M100

Datapro

Dataline

net.box

26 / 67

M

M

M

M100

Datapro

Dataline

net.box

require 'net.box'

--[[ tarantool to tarantool connector]]

27 / 67

M

M

M

M100

Datapro

Dataline

Ra�

Leader

Follower Follower

реализуемтольковыборлидера

28 / 67

Ra� Leader Elec�on (on lua)local r = self.pool.call(self.FUNC.request_vote, self.term, self.uuid)self._vote_count = self:count_votes(r)

if self._vote_count > self._nodes_count / 2 then log.info("[raft-srv] node %d won elections", self.id) self:_set_state(self.S.LEADER) self:_set_leader({ id=self.id, uuid=self.uuid }) self._vote_count = 0 self:stop_election_timer() self:start_heartbeater()else log.info("[raft-srv] node %d lost elections", self.id) self:_set_state(self.S.IDLE) self:_set_leader(msgpack.NULL) self._vote_count = 0 self:start_election_timer()end

29 / 67

Ref

Ref

M

M

M

Ref

M100

Datapro

Dataline

Leader

Follower Follower

30 / 67

Ref

Ref

M

M

M

Ref

M100

Datapro

Dataline

Leader

Follower Follower

тольколидер

раздаётзадачи

x 1запросов

31 / 67

Ref

Ref

M

M

M

Ref

M100

Datapro

Dataline

Leader

Abandoned FollowerRa�

?

случился split

две ноды ok,одна оторвана

32 / 67

x = 1x = 1

x := 2 x := 3

x = 3 x = 2

x = 2 x = 3

M-Mреплицирует

все изменения

33 / 67

x = 1x = 1

x := 2 x := 3

x = 3 x = 2

x = 2 x = 3

M-Mреплицирует

все изменения

консистентность?

34 / 67

OAuth Token

Refresh-T

Access-T

∞*

1 hToken

Refresh( Refresh-T ) = Access-T1

Refresh( Refresh-T ) = Access-T2

...

:

:

35 / 67

R+A1

Leader Follower

replicate R+A1

splitR+A2 R+A3

split ends

R+A3 R+A2

R+A4 replicate R+A4

Abandoned

refresherработает ис leader и сabandoned

36 / 67

Обычносвязанных нод

N/2 + 1запросов max

1x

Доступность

37 / 67

Обычносвязанных нод

N/2 + 1запросов max

1x

У нассвязанных нод

1запросов max

Nx

Доступность

38 / 67

100% CPU

Осталась ещё одна проблема

39 / 67

100% CPU Sharding!

Осталась ещё одна проблема

40 / 67

100% CPU Sharding!

Осталась ещё одна проблема

Кто знает что такое шардинг?

41 / 67

100% CPU Sharding!

Осталась ещё одна проблема

Кто знает что такое шардинг?

Кто делал свой шардинг?

42 / 67

Shard #2

Master 2

Slave 2

f(key) ➝ shard #

Shard #1

Master 1

Slave 1

43 / 67

Client1 f(key) Client2f(key)

Shard #1

Shard #2

Shard #N

44 / 67

Client1 Client2

Shard #1

Shard #2

Shard #N

f(key)

f(key)

f(key)

45 / 67

Shard #1

M

M M M

MM

Leader

Leader

Shard #2

46 / 67

Shard #1

M

M M M

MM

f(key) = sh #1

Leader

Leader

Shard #2

47 / 67

Shard #1

M

M M M

MM

f(key) = sh #2

Leader

Leader

Shard #2

48 / 67

А не многоли соединений?

49 / 67

Проблема Full mesh:

N2 соединенийN × (N-1)

50 / 67

Shard #1

M

M M M

MM

Leader

Leader

Shard #2

P P

51 / 67

N × constconst = p + n - 1

(n = 3, p = 3)

52 / 67

MM + Ra� + Shard + Proxy = Cluster

53 / 67

F F

R R... Rab Rab...

1 1TokensCluster

Address BookCluster

54 / 67

Shard #1

M

M M M

MM

Leader

Leader

Shard #2

P P

55 / 67

to refresh no need to refreshexpired

60sfirst

order

old agesecondorder

4 minthirdorder

1 hour

expira�on �me index

Refresh

56 / 67

no�fy

put()

index

lookup or wait

waittake() session

stash

disconnect

channel

id status payload�me

pk

57 / 67

queue:putfunction put(data) local t = box.space.queue:auto_increment({ 'r', --[[ status ]] util.time(), --[[ time ]] data --[[ any payload ]] })

return tend

58 / 67

queue:takefunction take(timeout) local start_time = util.time() local q_ind = box.space.tokens.index.queue local _,t

while true do local it = util.iter(q_ind, {'r'}, { iterator = box.index.GE }) _,t = it() if t and t[F.tokens.status] ~= 't' then break end

local left = (start_time + timeout) - util.time() if left <= 0 then return end t = q:wait(left) if t then break end end t = q:taken(t) return tend

59 / 67

queue:takenfunction queue:taken(task) local sid = box.session.id() if self._consumers[sid] == nil then self._consumers[sid] = {} end local k = task[self.f_id] local t = self:set_status(k, 't')

self._consumers[sid][k] = { util.time(), box.session.peer(sid), t } self._taken[k] = sid return tend

60 / 67

queue:on_disconnectfunction on_disconnect() local sid = box.session.id local now = util.time()

if self._consumers[sid] then local consumers = self._consumers[sid] for k,rec in pairs(consumers) do time, peer, task = unpack(rec)

local v = box.space[self.space].index[self.index_primary]:get({k})

if v and v[self.f_status] == 't' then v = self:release(v[self.f_id]) end end self._consumers[sid] = nil endend

61 / 67

Возможно лиальтернативное решение?

62 / 67

Shard #1

Master 1

Slave 1

Shard #2

Master 2

Slave 2

DB Queue

Q3

Q2

Quorum

Validator

Q1

63 / 67

ИтогПроблема outage решена

Горизонтальное масштабирование

Проблема N2 сведена к N×const

Релизована логика на очереди под бизнес-задачу

64 / 67

Получено очень много опыта принаписании собственной реализации

Ra�

65 / 67

Get yourdata in RAM

Computeclose to data

Enjoy theperformance

66 / 67

tarantool.orgDocumenta�on:

try.tarantool.orgTry it online:

github.com/tarantoolExplore it:

Ques�ons?

67 / 67