CodeFest 2013. Русанов П. — Есть ли жизнь в оффлайне? Кеш, транзакционный лог и проблемы синхронизации

439
-1

Published on

http://2013.codefest.ru/doklad/60

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
439
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
3
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

CodeFest 2013. Русанов П. — Есть ли жизнь в оффлайне? Кеш, транзакционный лог и проблемы синхронизации

  1. 1. Есть ли жизнь воффлайне?Кеш, транзакционный логи проблемысинхронизацииРусанов ПетрLinguaLeohttp://lingualeo.com
  2. 2. Зачем нам оффлайн еслиинтернет есть везде?
  3. 3. В городах полно Wi-Fi спотов
  4. 4. На улице есть 3G
  5. 5. Интернет есть даже втранспорте
  6. 6. И в самых неожиданныхместах• На Северном полюсе• На вершине горы Эверест• В пустынях ОАЭ• На МКС
  7. 7. Вроде бы все прекрасно,все счастливы и можноне париться
  8. 8. К сожалению, реальностьтакова:• 3G доступен только в больших городах, покрытие не 100%• 3G не везде безлимитный, порой очень дорогой• В роуминге цены на 3G достигают безумных цифр• Интернет в самолетах стоит примерно ~$20 за 10 Mб, средняяскорость ~0.05 Мбит/сек• В метро связь доступна только на станциях и то не на всех• Даже домашний интернет могут отключить, если забыл оплатить :)
  9. 9. История из жизни
  10. 10. Лечу в Новосибирск наCodeFest
  11. 11. Лететь долго, занятьсянечемВыучу пожалуйпару фраз наиспанском
  12. 12. WHUUUT??
  13. 13. Итого• Пользователю все равно: находится он вонлайне или нет, доступна сейчас сеть илипропала• Пользователи расстраиваются еслиприложение перестает функционировать вотсутствии интернета• При дальних перелетах/поездках/в метро улюдей полно свободного времени, но нетинтернета — это НУЖНО использовать,особенно в образовательных приложениях, гдельвиную долю функций можно реализовать безнеобходиомости постоянного доступа к сети
  14. 14. Есть решение!• Кеширование медиа-файлов• Хранение всех пользовательскихданных в локальной БД• Сохранение действий пользователя,произведенных в оффлайне• Синхронизация с сервером припоявлении доступа к сети
  15. 15. Кеширование
  16. 16. Кеши бывают разныеТребования1. Возможность хранения данныхне только в памяти, но и надиске2. Поддержка политик(изменяемое поведение дляразных типов запросов)3. Строгое следование заголовкамHTTP, обозначающим правилакеширования и перевалидации(Cache-Control, Expires, Last-Modified)4. Возможность отправкиconditional GETHTTP code: 200Age: 0Via: 1.1 varnishX-Varnish: 1103200550Accept-Ranges: bytes, bytesLast-Modified: Tue, 11 Sep 2012 14:01:07 GMTContent-Type: audio/mpegConnection: keep-aliveExpires: Tue, 26 Mar 2013 18:32:41 GMTServer: nginx/1.2.7Content-Length: 4752Date: Tue, 19 Mar 2013 18:32:41 GMTCache-Control: max-age=604800
  17. 17. Что кешировать?• НЕ нужно кешировать всё - долго изанимает много места на диске• Данные, без которых оффлайн работабудет невозможна• Опциональные наборы данных лучшекешировать по запросу пользователя
  18. 18. Политики кеширования•Политика определяет поведение при наличии закешированных данныхи при ошибке•Оффлайн режим для разработчика по сути представляет собойвыполнение всех запросов с ошибкой•Для запросов данных, которые предполагается кешировать дляоффлайн работы, важно указать именно политику при ошибке (напр. вASIHTTPRequest для iOS это ASIFallbackToCacheIfLoadFailsCachePolicy)•В оффлайне лучше показать устаревшие данные, чем ошибку
  19. 19. Итого• Выбираем правильную библиотеку длякеширования, соответствующуюописанным требованиям• Выставляем правильные политики длякешируемых запросов• Реализуем выборочное кеширование ипо запросу пользователя
  20. 20. Сохраняем историюдействий пользователя
  21. 21. Я знаю, что вы сделалипрошлым летомПока пользователь находитсявне зоны доступа, кромеиспользования закешированныхданных важно хранить историюего действий для последующейсинхронизации с серверомДля этого нужно понятное,автоматизированное ипрозрачное решение, которое нетребует значительныхизменений в коде
  22. 22. Не нужно изобретатьвелосипедМожно использовать принцип, аналогичный триггерам втранзакционных SQL или хукам вVCS (post commit):ПользовательсовершилдействиеИзменилсяобъект в БДПроизошелcommit в базуСработалpost-commithookИзменениесохранилось в лог
  23. 23. • Доступ к данным осуществляется исключительно через Data AccessObjects (DAO), полностью инкапсулирующих в себе работу с БД• Через proxy-классы перехватываются методы DAO для добавления/удаления/изменения объектов и устанавливаются transactionlistener-ы в качестве post-commit хуков (триггеров)• Сразу после коммита в БД вызываются transaction listener-ы исохраняют изменения в БД в виде записей транзакционного лога,содержащие тип операции (добавление/удаление/изменение),таймштамп, сам измененный объект и вспомогательные данные• Взаимоисключающие и дополняющие операции объединяются длякомпактности данныхРеализация: как это сделанов LinguaLeo
  24. 24. Реализация: DAO ивложенные транзакцииВ каждом объекте DAO имеется экземпляр объекта транзакцииВ основе объекта транзакции лежит счетчик открытых и закрытыхтранзакций (аналогично счетчику ссылок retainCount у NSObject вObjective-C)При выполнении [transaction begin] счетчик увеличиваетсяПри выполнении [transaction commit] счетчик уменьшаетсяКак только счетчик стал равен нулю, выполняется реальный commit вбазу данных и сразу после выполняется метод didCommitTransaction увсех transaction listener-ов
  25. 25. Пример: transaction listenerи DAO@implementation LLLoggingOfAddingBonusTransactionListener- (void)didCommittedTransaction {LLTransactionLogRecord *record = [LLTransactionLogRecord bonusLog];record.operation = @(LLOperationTypeInsert);record.loggedEntity = self.bonus;[self.transactionLogDAO insertRecord:record];}@end@protocol LLQuestDAO <LLDAO>- (void)deleteAllBonuses;- (NSArray *)bonusesForType:(LLQuestType)type;- (void)insertBonus:(LLBonus *)bonus;- (void)udpateBonus:(LLBonus *)bonus;- (LLQuest *)currentQuestStateForType:(LLQuestType)type;- (void)updateCurrentQuestState:(LLQuest *)questState forType:(LLQuestType)type;@end
  26. 26. Пример: proxy-класс дляDAO@implementation LLAOPLoggingDAOProxy+ (id)newProxyForWordDAO:(id <LLWordDAO>)wordDAO {id result = [[[LLAOPLoggingDAOProxy alloc] initWithInstance:wordDAO] autorelease];[result interceptMethodEndForSelector:@selector(insertWordsToUserDictionary:)interceptorSelector:@selector(logAddingNewWords:)];[result interceptMethodEndForSelector:@selector(insertWordsFromGlossaryToUserDictionary:)interceptorSelector:@selector(logAddingNewWordFromSimpleWords:)];[result interceptMethodStartForSelector:@selector(updateWord:)interceptorSelector:@selector(logUpdatingWord:)];[result interceptMethodStartForSelector:@selector(updateWords:)interceptorSelector:@selector(logUpdatingWords:)];[result interceptMethodStartForSelector:@selector(deleteWordsFromUserDictionary:)interceptorSelector:@selector(logDeletingWords:)];[result interceptMethodStartForSelector:@selector(updateSimpleWords:asKnown:)interceptorSelector:@selector(logUpdatingSimpleWord:)];return result;}@end
  27. 27. Пример: транзакционный логВ итоге лог выглядит примерно так:[{data: { ... },//поля объекта класса LLBonusentity_type:1,//тип сущности (LLBonus)operation:0,//тип операции (0 - вставка, 1 - обновление, 2 - удаление)timestamp:2013-02-20T10:33:46+0200//таймштамп},{data: { ... },entity_type:0,//LLWordoperation:1,//обновитьtimestamp:2013-02-21T11:25:40+0200}...]
  28. 28. Итого• Всю работу с БД осуществляем вDAO• Сохраняем все действияпользователя в лог не смотря наналичие/отсутствие сети• Стараемся сделать лог компактным,объединяя записи
  29. 29. Синхронизация
  30. 30. Да будет сеть!Как только появляется доступ к сети,нужно синхронизовать данные ссервером:• отправить на сервер сохраненныеоперации• принять операции от сервера,совершенные на сайте и другихустройствах• данных могло накопиться много -нужно упаковать данные, чтобысинхронизация прошла быстро и не"съела" много траффика
  31. 31. Проблемы синхронизации У пользователя может быть несколькоустройствКакие-то из них могут находиться воффлайнеВ них будет накапливаться историяопераций на основе устаревшихданныхПри выходе этих устройств в сетьмогут возникать конфликты
  32. 32. Ничего не напоминает?
  33. 33. Принцип VCS длясинхронизации Это самый обычный кейс для системконтроля версий вроде SVN или GitВсе решается вводом численногообозначения ревизии данных (или хеша),которое контролирует серверПри синхронизации:Если на устройстве устаревшая версияданных, сначала накатываются и мержатсяизменения с сервера (svn up/git pull) итолько затем делается аплоад накопившихсяопераций на сервер (svn commit/git push)
  34. 34. Синхронизация: схема VCSrev 0registerrev 0loginrev 0добавить ‘get up’с переводом ‘вставать’добавить ‘get up’с переводом ‘дорожать’syncpushrev 1syncpushclientrevisonis old!syncpullMERGEwithrev 1syncpushrev 2syncpullrev 2обновить ‘get up’:перевод ‘дорожать’rev 2
  35. 35. Итого• Регулярно делаем синхронизацию,чтобы минимизировать объемотправляемых данных за раз• Применяем методы синхронизациииз систем контроля версий• При неудачной синхронизацииделаем rollback
  36. 36. Русанов Петр,LinguaLeohttp://lingualeo.comprusanov@lingualeo.com+7 (920) 0269579яВопросы?
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×