24

Роман Иманкулов-«Быстрые и масштабируемые приложения с Sync API»

Embed Size (px)

Citation preview

План доклада

• Начать с какого-нибудь банального утверждения

• Раскритиковать проверенную временем технологию

• Представить собственное сомнительное решение

• ???

• PROFIT!!!

Все приличные веб-сервисы предоставляют RESTful API

C RESTful API что-то не так!

Response Times: The 3 Important Limits

• до 0.1 секундыПользователь манипулирует объектами напрямую

• до 1 секундыПользователь замечает задержку, но может работать с интерфейсом

• 10 секундПредел терпения

Источник: http://www.nngroup.com/articles/response-times-3-important-limits/

Индексы для коллекций ресурсов

Например github.com issues GET /issuesGET /user/issuesGET /orgs/:org/issuesGET /repos/:owner/:repo/issues

(и фильтры для milestone, state, assignee, creator, labels …)

Search API?ElasticSearch, Solr, Sphinx?

Sync APIMySQL, Redis, мозг

Sync API• Дополняет, а не заменяет RESTful API

• Клиенты: толстые, локально хранят копию данных с сервера

• Сервер: туповат, просто предоставляет API для получения кусочков данных

• Примеры применения: • списки задач, заметок и т.д. • панели управления ресурсами • системы мониторинга, мгновенных уведомлений

Sync API в Todoist• Основной API для всех официальных клиентов c 2012 года

• Работает на Redis и MySQL

• Обслуживает 1’000’000 активных пользователей

• Синхронизирует задачи между устройствами за 5 секунд

• Поддерживается командой из трех разработчиков

Sync API. Реализация

id content … deleted seq_no

1 foo FALSE 56

2 null TRUE 83

Таблица с объектами

seq_no:<uid1> 85

seq_no:<uid2> 167

Счетчик в Redis для каждого пользователя

Sync API. INSERT/UPDATE/DELETEПолучить следующий Sequence Number INCR seq_no:<uid>

Манипулируя объектом, не забыть обновить seq_no INSERT INTO table (…, seq_no) VALUES (..., <seq_no>) UPDATE table SET … seq_no = <seq_no> UPDATE table SET … is_deleted = TRUE, seq_no = <seq_no>

Sync API. Первоначальная синхронизацияЗапрос GET /api/sync

Подготовка ответа MySQL: SELECT ... WHERE user_id=<uid> AND deleted=FALSE Redis: GET seq_no:<uid>

Ответ {"seq_no": <seq_no>: "objects": [...]}

Sync API. Частичная синхронизацияЗапрос GET /api/sync?seq_no=1234

Подготовка ответа MySQL: SELECT ... WHERE user_id=<uid> AND seq_no > 1234 Redis: GET seq_no:<uid>

Ответ {"seq_no": <seq_no>: "objects": [...]}

Sync API. Почему это хорошо

• Простая реализация

• Данные рядом с клиентом

• Один индекс в базе

• Push уведомления

Sync API. Что можно сделать лучше?

• Не хотим хранить удаленные объекты в базе!

• Не хотим B-tree индекс, который O(log n)

Синхронизация master-slave в Redis

Учимся у @antirez

slave master клиентыsync

создаем буфер

готовим данные для отправки

Буфер

1 2 3 4

команды: A, B, C

A B C

возвращаем данные,

A, B, C, offset=3команды: D, E, F

DE F5 6

sync, offset=3

DEF, offset=6

Sync API v2. Redis backlogСчетчик. Храним seq_no самого последнего сообщения в очереди INCR seq_no:<uid>

Очередь. Добавляем id измененного объекта и подрезаем очередь RPUSH queue:<user_id> <object_id> LTRIM queue:<user_id> -1 -100

СерверКлиент Другие клиенты

счетчик буфер (redis list, max length: 4, хранит только ids)

создать объекты A, B, C

3

GET /api/sync

{seq_no: 3, objects: [A, B, C]}создать D, изменить А, B

A1

B2

C3

D4

A5

B6

6

GET /api/sync?seq_no=3

{seq_no: 6, objects: [D, A, B]}

...GET /api/sync?seq_no=1

Error: full sync required

Sync API v2. Redis backlog• Индексы только по id и user_id.

Время поиска и вставки O(1)

• Очередь фиксированной длины, и только для активных клиентов. Память О($), где $ — количество платящих клиентов, 1.5Kb на очередь из 200 сообщений 11 млн клиентов на 16Gb (m4.xlarge, 126$ в месяц)

• Клиент должен быть готов к "полной перезагрузке"

• Используется Evernote и Google Calendar?

Sync API. Ограничения localStorageUser Agent Размер (тыс

символов)Чтение из 10k (мс)

Чтение из 500k (мс)

Чтение из макс (мс)

Chrome 33 5,120 3 20 1,026

Chrome Mobile 32

5,120 28 164 1,066

Firefox 27 5,120 1 14 143

IE 11 4,883 15 32 872

Opera 19 5,120 7 36 313

Mobile Safari 7

2,560 16 37 104

Safari 7 2,560 11 44 177

Источник: https://www.stevesouders.com/blog/2014/02/11/measuring-localstorage-performance/

Ограничения localStorage. Что делать?

• Считывать данные в память при первой загрузкедля ускорения доступа

• Не хранить данные в JSONпреобразование списка объектов в CSV экономит до 50% места

• Сжимать данныеСм. http://pieroxy.net/blog/pages/lz-string/index.html, до 95%

• Использовать вытесняющий кеш

Жизнеутверждающий финалSync API — это

• Простая реализация

• Данные рядом с клиентом

• Redis: память O(n), n — количество активных клиентов

• Бесплатные push уведомления