Similar presentations:
Поддержка офлайна в мобильном приложении
1. Отправить всё! Поддержка офлайна в мобильном приложении. Рецепт от Solo
1Отправить всё!
Поддержка офлайна в мобильном
приложении. Рецепт от Solo
Владимир Абакумов
2. Цель
2Цель
Поделиться подходом к неблокирующей отправке созданных
пользователем данных на сервер
3. О чём пойдёт речь
3О чём пойдёт речь
Как «хорошо» отправлять данные на сервер:
• концепция
• варианты реализации
• и ещё пара рецептов
4. Чего не будет
4Чего не будет
• Транзакционность
5. Интро
5Интро
• Владимир Абакумов, TeamLead ОМР
• DIRECTUM и DirectumRX
• DIRECTUM Solo и Jazz
6. Отправка созданных пользователем данных
6Отправка созданных пользователем данных
• Блокируем работу
7. Отправка созданных пользователем данных
7Отправка созданных пользователем данных
• Блокируем работу
• Просто и надежно
• Ответственность на пользователе
• Бесяче (╯ ° □ °) ╯ (┻━┻)
8. Отправка созданных пользователем данных
8Отправка созданных пользователем данных
• Не блокируем работу
9. Когда узнаешь, что данные пропали
9Когда узнаешь, что данные пропали
10. Отправка созданных пользователем данных
10Отправка созданных пользователем данных
• Не блокируем работу
• Не понятен статус
11. Отправка созданных пользователем данных
11Отправка созданных пользователем данных
• Не блокируем работу
• Не понятен статус
• Можно потерять данные
12. Отправка созданных пользователем данных
12Отправка созданных пользователем данных
• Не блокируем работу
• Не теряем данные
13. Приложение для организации событий
13Приложение для организации событий
14. Приложение для организации событий
14Приложение для организации событий
• Создаём событие, указывая реквизиты, участников
• Добавляем фото места
• Добавляем новых участников
15. Задачи
15Задачи
• Создать событие
• Добавить фото:
• Обработать фото (сжатие, фильтры);
• Отправить файл на сервер и получить URL;
• Прикрепить к событию.
• Добавить участников
16. Схема задач
16Схема задач
Создание
события
Создать
событие
Обработать
фото
Добавить
фото
Отправить
фото
Добавить
участников
Добавить к
событию
17. Схема задач
17Схема задач
Создание
события
Создать
событие
Обработать
фото
Добавить
фото
Отправить
фото
Добавить
участников
Добавить к
событию
18. Схема задач
18Схема задач
Создание
события
Создать
событие
Обработать
фото
Добавить
фото
Отправить
фото
Добавить
участников
Добавить к
событию
19. Схема задач
19Схема задач
Создание
события
Создать
событие
Обработать
фото
Добавить
фото
Отправить
фото
Добавить
участников
Добавить к
событию
20. Схема задач
20Схема задач
Создание
события
Создать
событие
Обработать
фото
Добавить
фото
Отправить
фото
Добавить
участников
Добавить к
событию
21. Схема задач
21Схема задач
Создание
события
Создать
событие
Обработать
фото
Добавить
фото
Отправить
фото
Добавить
участников
Добавить к
событию
22. Схема задач
22Схема задач
Создание
события
Создать
событие
Обработать
фото
Добавить
фото
Отправить
фото
Добавить
участников
Добавить к
событию
23. Схема задач
23Схема задач
24. Схема задач
24Схема задач
25. Схема задач
25Схема задач
Создание
события
Создать
событие
Обработать
фото
Добавить
фото
Отправить
фото
Добавить
участников
Добавить к
событию
26. Схема задач
26Схема задач
Создание
события
Создать
событие
Обработать
фото
Добавить
фото
Отправить
фото
Добавить
участников
Добавить к
событию
27. Схема задач
27Схема задач
Создание
события
Создать
событие
Обработать
фото
Добавить
фото
Отправить
фото
Добавить
участников
Добавить к
событию
28. Схема задач
28Схема задач
Создание
события
Создать
событие
Обработать
фото
Добавить
фото
Отправить
фото
Добавить
участников
Добавить к
событию
29. Отправка созданных пользователем данных
29Отправка созданных пользователем данных
• Разбиваем задачи на атомарные подзадачи
• Сохранение данных и состояния задач
• Семантически значимый ключ: EventID_TaskID_SubtaskID
• Value в виде примитивных данных или сериализуемых структур, которые
переживут изменения из-за обновлений сущностей
30. Запросы и их задачи
30Запросы и их задачи
• Request = намерение
• RequestTask = как его достичь
31. Приложение для организации событий
31Приложение для организации событий
• Создаём событие
• Добавляем фото места
• Добавляем новых участников
CreateEventRequest
AddPhotoRequest
AddParticipantRequest
32. Приложение для организации событий
32Приложение для организации событий
• Request -> RequestTask
33. Интерфейс RequestTask
33Интерфейс RequestTask
public interface IRequestTask
{
int Id { get; }
Request Request { get; }
ExecutingState State { get; }
Task RunAsync();
void Load(IReadOnlyDictionary<string, object> data);
IReadOnlyDictionary<string, object> Save();
}
34. Интерфейс RequestTask
34Интерфейс RequestTask
public interface IRequestTask
{
int Id { get; }
Request Request { get; }
ExecutingState State { get; }
Task RunAsync();
void Load(IReadOnlyDictionary<string, object> data);
IReadOnlyDictionary<string, object> Save();
}
35. Интерфейс RequestTask
35Интерфейс RequestTask
public interface IRequestTask
{
int Id { get; }
Request Request { get; }
ExecutingState State { get; }
Task RunAsync();
void Load(IReadOnlyDictionary<string, object> data);
IReadOnlyDictionary<string, object> Save();
}
36. Интерфейс RequestTask
36Интерфейс RequestTask
public interface IRequestTask
{
int Id { get; }
Request Request { get; }
ExecutingState State { get; }
Task RunAsync();
void Load(IReadOnlyDictionary<string, object> data);
IReadOnlyDictionary<string, object> Save();
}
37. Интерфейс RequestTask
37Интерфейс RequestTask
public interface IRequestTask
{
int Id { get; }
Request Request { get; }
ExecutingState State { get; }
Task RunAsync();
void Load(IReadOnlyDictionary<string, object> data);
IReadOnlyDictionary<string, object> Save();
}
38. Интерфейс RequestTask
38Интерфейс RequestTask
public interface IRequestTask
{
int Id { get; }
Request Request { get; }
ExecutingState State { get; }
Task RunAsync();
void Load(IReadOnlyDictionary<string, object> data);
IReadOnlyDictionary<string, object> Save();
}
39. Интерфейс RequestTask
39Интерфейс RequestTask
public interface IRequestTask
{
int Id { get; }
Request Request { get; }
ExecutingState State { get; }
Task RunAsync();
void Load(IReadOnlyDictionary<string, object> data);
IReadOnlyDictionary<string, object> Save();
}
40. Приложение для организации событий
40Приложение для организации событий
• CreateEventRequest
CreateEventRequestTask
• AddPhotoRequest
CompressPhotoRequestTask
SendPhotoRequestTask
AddPhotoToEventRequestTask
• AddParticipantRequest AddParticipantRequestTask
41. Очередь
41Очередь
CreateEvent
CompressPhoto
SendPhoto
AddPhotoToEvent
AddParticipants
42. Очередь
42Очередь
CreateEvent
CompressPhoto
Executor 1
SendPhoto
AddPhotoToEvent
AddParticipants
Executor 2
43. SerialRequestTask
43SerialRequestTask
public class SerialRequestTask : IRequestTask
{
private readonly IRequestTask _wrappedTask;
private readonly IRequestTask _dependingOnTask;
…
}
44. SerialRequestTask
44SerialRequestTask
public class SerialRequestTask : IRequestTask
{
private readonly IRequestTask _wrappedTask;
private readonly IRequestTask _dependingOnTask;
…
}
45. SerialRequestTask
45SerialRequestTask
public class SerialRequestTask : IRequestTask
{
private readonly IRequestTask _wrappedTask;
private readonly IRequestTask _dependingOnTask;
…
}
46. Очередь
CreateEventScheduler
CompressPhoto
AddParticipants
SendPhoto
46
Executor
1
Executor 2
AddPhotoToEvent
47. Очередь
CreateEventScheduler
CompressPhoto
Executor 1
AddParticipants
SendPhoto
47
Executor
1
Executor 2
AddPhotoToEvent
48. Очередь
CreateEventScheduler
CompressPhoto
Executor 1
AddParticipants
SendPhoto
48
Executor
1
Executor 2
AddPhotoToEvent
49. Очередь
CreateEventScheduler
CompressPhoto
Executor 1
AddParticipants
SendPhoto
49
Executor
1
Executor 2
AddPhotoToEvent
50. Очередь
CreateEventScheduler
CompressPhoto
Executor 1
AddParticipants
SendPhoto
50
Executor
1
Executor 2
AddPhotoToEvent
51. Очередь
51Очередь
CreateEvent
Scheduler
CompressPhoto
Executor 1
AddParticipants
Executor 2
SendPhoto
AddPhotoToEvent
52. Очередь
52Очередь
CreateEvent
Scheduler
CompressPhoto
Executor 1
AddParticipants
Executor 2
SendPhoto
AddPhotoToEvent
53. Очередь
53Очередь
CreateEvent
Scheduler
CompressPhoto
SendPhoto
Executor 1
AddParticipants
Executor 2
AddPhotoToEvent
54. Очередь
54Очередь
CreateEvent
Scheduler
CompressPhoto
SendPhoto
Executor 1
AddParticipants
Executor 2
AddPhotoToEvent
55. Очередь
55Очередь
Scheduler
Executor 2
CreateEvent
CompressPhoto
SendPhoto
Executor 1
AddParticipants
AddPhotoToEvent
56. Очередь
56Очередь
Scheduler
Executor 2
CreateEvent
CompressPhoto
SendPhoto
AddPhotoToEvent
Executor 1
AddParticipants
57. Очередь
57Очередь
Scheduler
Executor 2
CreateEvent
CompressPhoto
SendPhoto
AddPhotoToEvent
Executor 1
AddParticipants
58. Очередь
58Очередь
Scheduler
Executor 1
Executor 2
CreateEvent
CompressPhoto
AddParticipants
SendPhoto
AddPhotoToEvent
59. Очередь
[0] CreateEventScheduler
[ 1] CompressPhoto
[0] AddParticipants
[1] SendPhoto
59
Executor
1
Executor 2
[1] AddPhotoToEvent
60. Очередь
60Очередь
[0] CreateEvent
Scheduler
Executor 2
[ 1] CompressPhoto
Executor 1
[0] AddParticipants
[1] SendPhoto
[1] AddPhotoToEvent
61. Очередь
61Очередь
[0] CreateEvent
Scheduler
Executor 2
[ 1] CompressPhoto
Executor 1
[0] AddParticipants
[1] SendPhoto
[1] AddPhotoToEvent
62. Очередь
62Очередь
[0] CreateEvent
Scheduler
Executor 2
[ 1] CompressPhoto
[0] AddParticipants
Executor 1
[1] SendPhoto
[1] AddPhotoToEvent
63. Очередь
63Очередь
[0] CreateEvent
Scheduler
Executor 2
[ 1] CompressPhoto
[0] AddParticipants
Executor 1
[1] SendPhoto
[1] AddPhotoToEvent
64. Очередь
64Очередь
[0] CreateEvent
Scheduler
[ 1] CompressPhoto
Executor 2
[0] AddParticipants
Executor 1
[1] SendPhoto
[1] AddPhotoToEvent
65. Очередь
65Очередь
[0] CreateEvent
Scheduler
[ 1] CompressPhoto
[1] SendPhoto
Executor 2
[0] AddParticipants
Executor 1
[0] AddParticipants
[1] AddPhotoToEvent
66. Очередь
66Очередь
[0] CreateEvent
Scheduler
[ 1] CompressPhoto
[1] SendPhoto
Executor 2
[0] AddParticipants
Executor 1
[0] AddParticipants
[1] AddPhotoToEvent
67. Очередь
67Очередь
[0] CreateEvent
Scheduler
[ 1] CompressPhoto
[1] SendPhoto
[0] AddParticipants
[0] AddParticipants
Executor 1
Executor 2
[1] AddPhotoToEvent
68. Очередь
68Очередь
[0] CreateEvent
Scheduler
[ 1] CompressPhoto
[1] SendPhoto
[0] AddParticipants
[0] AddParticipants
Executor 1
Executor 2
[1] AddPhotoToEvent
69. Очередь
69Очередь
[0] CreateEvent
Scheduler
[ 1] CompressPhoto
[1] SendPhoto
[0] AddParticipants
[0] AddParticipants
Executor 1
Executor 2
[1] AddPhotoToEvent
70. Очередь
70Очередь
[0] CreateEvent
Scheduler
[ 1] CompressPhoto
[1] SendPhoto
Executor 1
[0] AddParticipants
[0] AddParticipants
Executor 2
[1] AddPhotoToEvent
71. Очередь
71Очередь
[0] CreateEvent
Scheduler
[ 1] CompressPhoto
[1] SendPhoto
Executor 1
[0] AddParticipants
[0] AddParticipants
Executor 2
[1] AddPhotoToEvent
72. Очередь
72Очередь
Scheduler
Executor 2
[0] CreateEvent
[ 1] CompressPhoto
[1] SendPhoto
[1] AddPhotoToEvent
Executor 1
[0] AddParticipants
[0] AddParticipants
73. Очередь
73Очередь
Scheduler
Executor 2
[0] CreateEvent
[ 1] CompressPhoto
[1] SendPhoto
[1] AddPhotoToEvent
Executor 1
[0] AddParticipants
[0] AddParticipants
74. Очередь
74Очередь
Scheduler
Executor 1
Executor 2
[0] CreateEvent
[ 1] CompressPhoto
[0] AddParticipants
[1] SendPhoto
[0] AddParticipants
[1] AddPhotoToEvent
75. Преимущества
75Преимущества
• Удобно разрабатывать
• Гарантированная доставка
• Просто визуализировать
76. Недостатки: сложно
76Недостатки: сложно
77. Недостатки
77Недостатки
• Необходимость синхронизировать очередь
• Сложный, витиеватый код
78. И ещё пара рецептов
78И ещё пара рецептов
79. Взаимоисключение задач
79Взаимоисключение задач
AddParticipant
80. Взаимоисключение задач
80Взаимоисключение задач
AddParticipant
RemoveParticipant
81. Взаимоисключение задач
81Взаимоисключение задач
public class AddParticipantRequest : Request
{
public int EventId { get; }
public int UserId { get; }
AddParticipant
public override RequestConnectedType GetConnectedType(Request
anotherRequest) =>
anotherRequest is RemoveParticipantRequest r && r.UserId == UserId
? RequestConnectedType.MutuallyExclusive
: base.GetConnectedType(anotherRequest);
}
RemoveParticipant
82. Взаимоисключение задач
82Взаимоисключение задач
public class AddParticipantRequest : Request
{
public int EventId { get; }
public int UserId { get; }
AddParticipant
public override RequestConnectedType GetConnectedType(Request
anotherRequest) =>
anotherRequest is RemoveParticipantRequest r && r.UserId == UserId
? RequestConnectedType.MutuallyExclusive
: base.GetConnectedType(anotherRequest);
}
RemoveParticipant
83. Взаимоисключение задач
83Взаимоисключение задач
public class AddParticipantRequest : Request
{
public int EventId { get; }
public int UserId { get; }
AddParticipant
public override RequestConnectedType GetConnectedType(Request
anotherRequest) =>
anotherRequest is RemoveParticipantRequest r && r.UserId == UserId
? RequestConnectedType.MutuallyExclusive
: base.GetConnectedType(anotherRequest);
}
RemoveParticipant
84. Взаимоисключение задач
84Взаимоисключение задач
public class AddParticipantRequest : Request
{
public int EventId { get; }
public int UserId { get; }
AddParticipant
public override RequestConnectedType GetConnectedType(Request
anotherRequest) =>
anotherRequest is RemoveParticipantRequest r && r.UserId == UserId
? RequestConnectedType.MutuallyExclusive
: base.GetConnectedType(anotherRequest);
}
RemoveParticipant
85. Взаимоисключение задач
85Взаимоисключение задач
public class AddParticipantRequest : Request
{
public int EventId { get; }
public int UserId { get; }
AddParticipant
public override RequestConnectedType GetConnectedType(Request
anotherRequest) =>
anotherRequest is RemoveParticipantRequest r && r.UserId == UserId
? RequestConnectedType.MutuallyExclusive
: base.GetConnectedType(anotherRequest);
}
RemoveParticipant
86. Взаимоисключение задач
86Взаимоисключение задач
AddParticipant
RemoveParticipant
87. Взаимоисключение задач
87Взаимоисключение задач
88. Дедупликация данных
88Дедупликация данных
Создание
события
Ошибка
сети
Создание
события
89. Дедупликация данных
89Дедупликация данных
Создание
события
Создаю
Ошибка
сети
Создал
Создание
события
Создал
90. Дедупликация данных
90Дедупликация данных
Создание
события
Создаю
Ошибка
сети
Создал
Создание
события
Создал
91. Дедупликация данных
91Дедупликация данных
Создание
события
Создаю
Ошибка
сети
Создал
Создание
события
Создал
92. Дедупликация данных
92Дедупликация данных
Создание
события
Создаю
Ошибка
сети
Создал
Создание
события
Создал
93. Дедупликация данных
93Дедупликация данных
Создание
события
Создаю
Ошибка
сети
Создал
Создание
события
Создаю
94. Дедупликация данных
94Дедупликация данных
Создание
события
RequestId
Создаю
Ошибка
сети
Создал
Создание
события
Да создал
уже!
95. Дедупликация данных
95Дедупликация данных
Создание
события
RequestId
Ошибка
сети
Создание
события
Создаю
Создал
RequestId
Да создал
уже!
96. Итоги
96Итоги
• Разбиваем задачи на атомарные подзадачи
• Сохранение и восстановление данных и состояния
• Связанные задачи выполняем последовательно
• Приоритеты задач
• Взаимоисключение задач
• Отправка RequestID на сервер и кэширование результата запроса
на сервере по этому RequestID
97. Спасибо
97Спасибо