SlideShare a Scribd company logo
View как чистая функция
от состояния базы
данных
Илья Беда / bro.agency
Кто я?
● Тимлид и один из основателей bro.agency
● Разрабатываю веб-формочки на python в течении 7
лет
● Специализируюсь на аутсорс-разработке
● Продвигаю функциональное программирование в
массы
● Провожу воркшопы и мастер-классы
● С недавнего времени выступаю на конференциях
2
There are only two hard things in Computer
Science: cache invalidation and naming things.
Phil Karlton
3
Какая нам нужна инвалидация кэша?
4
Инвалидация по умолчанию (по таймауту)
5
Инвалидация по умолчанию (по таймауту)
6
Слишком часто Слишком редко
Инвалидация по умолчанию (по таймауту)
7
Слишком часто Слишком редко
Кэш становится не
актуальным, но сами
данные остались
неизменными
Пользователь увидит не
актуальную версию
страницы
Инвалидация по умолчанию (по таймауту)
8
Слишком часто Слишком редко
Кэш становится не
актуальным, но сами
данные остались
неизменными
Пользователь увидит не
актуальную версию
страницы
9
Какую инвалидацию использовать для таких
приложений?
10
Как работает ВЕБ
11
Что делает Handler?
12
Получение
данных из базы
Формирование
контекста
Рендер
шаблона
Что делает Handler?
13
Запросы к
внешнему API
Формирование
контекста
json.dumps
И handler очень похож на чистую функцию от
состояния базы данных
14
Получение
данных из базы
Формирование
контекста
Рендер
шаблона
Какие функции являются чистыми?
15
Какие функции являются чистыми?
1. Детерминированные функции
2. Функции не обладающие сторонними эффектами
16
Чистая функция
17
Не чистая функция
18
В чем преимущества чистых функций?
19
В чем преимущества чистых функций?
Для одного и того же набора аргументов она
возвращает одно и тоже значение.
Что упрощает проблему инвалидации
20
Давайте сделаем наш ВЕБ функционально
чистым и забудем про проблемы
инвалидации кэша.
21
Как это выглядит в реальности (Django)
22
1
2
3
4
5
def some_view(request, url_param):
items = SomeModel.objects.filter(some_field=url_param)
c = Context({'items': items})
response = Template("my_template.html").render(c)
return HttpResponse(response)
Как это выглядит в реальности (Django)
23
1
2
3
4
5
def some_view(request, url_param):
items = SomeModel.objects.filter(some_field=url_param)
c = Context({'items': items})
response = Template("my_template.html").render(c)
return HttpResponse(response)
Как это выглядит в реальности (Django)
24
1
2
3
4
5
def some_view(request, url_param):
items = SomeModel.objects.filter(some_field=url_param)
c = Context({'items': items})
response = Template("my_template.html").render(c)
return HttpResponse(response)
Как это выглядит в реальности (Django)
25
1
2
3
4
5
def some_view(request, url_param):
items = SomeModel.objects.filter(some_field=url_param)
c = Context({'items': items})
response = Template("my_template.html").render(c)
return HttpResponse(response)
Проанализируем с точки зрения чистоты
26
1
2
3
4
5
def some_view(request, url_param):
items = SomeModel.objects.filter(some_field=url_param)
c = Context({'items': items})
response = Template("my_template.html").render(c)
return HttpResponse(response)
Проанализируем с точки зрения чистоты
27
1
2
3
4
5
def some_view(request, url_param):
items = SomeModel.objects.filter(some_field=url_param)
c = Context({'items': items})
response = Template("my_template.html").render(c)
return HttpResponse(response)
Получается наш ВЕБ
НЕ
функционально чистый.
28
Приведем функцию к чистому виду
29
1
2
3
4
5
6
7
8
9
10
def some_view(request, url_param):
items = list(
SomeModel.objects.filter(some_field=url_param))
@cache_pure
def _some_view_pure(object_list):
c = Context({'items': object_list})
response = Template("my_template.html").render(c)
return HttpResponse(response)
return _some_view_pure(items)
Приведем функцию к чистому виду
30
1
2
3
4
5
6
7
8
9
10
def some_view(request, url_param):
items = list(
SomeModel.objects.filter(some_field=url_param))
@cache_pure
def _some_view_pure(object_list):
c = Context({'items': object_list})
response = Template("my_template.html").render(c)
return HttpResponse(response)
return _some_view_pure(items)
Приведем функцию к чистому виду
31
1
2
3
4
5
6
7
8
9
10
def some_view(request, url_param):
items = list(
SomeModel.objects.filter(some_field=url_param))
@cache_pure
def _some_view_pure(object_list):
c = Context({'items': object_list})
response = Template("my_template.html").render(c)
return HttpResponse(response)
return _some_view_pure(items)
Давайте пофантазируем
32
1
2
3
4
5
6
7
8
9
10
11
def some_view(request, url_param):
@cache_pure
def _some_view_pure(url_param, database_state):
items = SomeModel.objects.filter(
some_field=url_param)
c = Context({'items': items})
response = Template("my_template.html").render(c)
return HttpResponse(response)
return _some_view_pure(
url_param,
get_database_state_from_magic())
Давайте пофантазируем
33
1
2
3
4
5
6
7
8
9
10
11
def some_view(request, url_param):
@cache_pure
def _some_view_pure(url_param, database_state):
items = SomeModel.objects.filter(
some_field=url_param)
c = Context({'items': items})
response = Template("my_template.html").render(c)
return HttpResponse(response)
return _some_view_pure(
url_param,
get_database_state_from_magic())
Давайте пофантазируем
34
1
2
3
4
5
6
7
8
9
10
11
def some_view(request, url_param):
@cache_pure
def _some_view_pure(url_param, database_state):
items = SomeModel.objects.filter(
some_field=url_param)
c = Context({'items': items})
response = Template("my_template.html").render(c)
return HttpResponse(response)
return _some_view_pure(
url_param,
get_database_state_from_magic())
def get_database_state_from_magic():
Что же это такое состояние базы данных?
35
def get_database_state_from_magic():
return subprocess.check_output(["pg_dump"])
36
def get_database_state_from_magic():
Если пренебречь тем, что база данных может
вернуться в предыдущее состояние, то можно
использовать индекс поколений.
И это практичное решение проблемы, которое можно
использовать.
37
38
News News News
DB State 1 DB State 2 DB State 3
Реализация индексирования поколений
39
1
2
3
4
5
6
7
8
9
10
11
import redis
from django.db.models.signals import post_save
r = redis.StrictRedis(host='localhost', port=6379, db=0)
def inc_generation_index(sender, **kwargs):
r.incr('generation_index')
post_save.connect(inc_generation_index)
def get_database_state_from_magic():
return r.get('generation_index')
Реализация индексирования поколений
40
1
2
3
4
5
6
7
8
9
10
11
import redis
from django.db.models.signals import post_save
r = redis.StrictRedis(host='localhost', port=6379, db=0)
def inc_generation_index(sender, **kwargs):
r.incr('generation_index')
post_save.connect(inc_generation_index)
def get_database_state_from_magic():
return r.get('generation_index')
Реализация индексирования поколений
41
1
2
3
4
5
6
7
8
9
10
11
import redis
from django.db.models.signals import post_save
r = redis.StrictRedis(host='localhost', port=6379, db=0)
def inc_generation_index(sender, **kwargs):
r.incr('generation_index')
post_save.connect(inc_generation_index)
def get_database_state_from_magic():
return r.get('generation_index')
Реализация индексирования поколений
42
1
2
3
4
5
6
7
8
9
10
11
import redis
from django.db.models.signals import post_save
r = redis.StrictRedis(host='localhost', port=6379, db=0)
def inc_generation_index(sender, **kwargs):
r.incr('generation_index')
post_save.connect(inc_generation_index)
def get_database_state_from_magic():
return r.get('generation_index')
Реализация индексирования поколений
43
1
2
3
4
5
6
7
8
9
10
11
import redis
from django.db.models.signals import post_save
r = redis.StrictRedis(host='localhost', port=6379, db=0)
def inc_generation_index(sender, **kwargs):
r.incr('generation_index')
post_save.connect(inc_generation_index)
def get_database_state_from_magic():
return r.get('generation_index')
И теперь это реально работающий код
44
1
2
3
4
5
6
7
8
9
10
11
def some_view(request, url_param):
@cache_pure
def _some_view_pure(url_param, database_state):
items = SomeModel.objects.filter(
some_field=url_param)
c = Context({'items': items})
response = Template("my_template.html").render(c)
return HttpResponse(response)
return _some_view_pure(
url_param,
get_database_state_from_magic())
Какие могут быть проблемы
при индексировании поколений?
45
Слишком частая инвалидация.
Кэш становится неактуальным, но сами
данные остались неизменными
46
47
current_news
News.objects.filter(hidden=False)
news_comemnts
Comment.objects.filter(news_id=1, hidden=False)
News Comment
48
current_news
News.objects.filter(hidden=False)
news_comemnts
Comment.objects.filter(news_id=1, hidden=False)
News Comment
+ Comment
49
current_news
News.objects.filter(hidden=False)
news_comemnts
Comment.objects.filter(news_id=1, hidden=False)
News Comment
Comment
50
current_news
News.objects.filter(hidden=False)
news_comemnts
Comment.objects.filter(news_id=1, hidden=False)
News Comment
Comment
51
current_news
News.objects.filter(hidden=False)
news_comemnts
Comment.objects.filter(news_id=1, hidden=False)
News Comment
Comment
hidden = True
Гранулярная инвалидация сделает
кэширование оптимальнее.
52
Можно индексировать поколения каждой
таблицы отдельно.
https://github.com/jmoiron/johnny-cache
53
54
current_news
News.objects.filter(hidden=False)
news_comemnts
Comment.objects.filter(news_id=1, hidden=False)
News Comment
Comment
55
В реальности очень много таблиц, поэтому это будет
прекрасно работать.
News Comment Page Event User
Можно пойти дальше и инвалидировать кэш,
зависящий от определенной части состояния базы
данных.
Тогда нам нужен инструмент для описания этой
части.
56
57
current_news
News.objects.filter(hidden=False)
news_comemnts
Comment.objects.filter(news_id=1, hidden=False)
News Comment
58
current_news
News.objects.filter(hidden=False)
news_comemnts
Comment.objects.filter(news_id=1, hidden=False)
News Comment
Можно индексировать части запросов
59
News.hiden=False
Comment.news_id=1
Comment.hidden=False
Cache Store Criterion
Можно индексировать части запросов
60
News.hiden=False
Comment.news_id=1
Comment.hidden=False
Cache Store Criterion
+ Comment
hiden True
news_id 1
Можно индексировать части запросов
61
News.hiden=False
Comment.news_id=1
Comment.hidden=False
Cache Store Criterion
+ Comment
hiden True
news_id 1
Можно индексировать части запросов
62
News.hiden=False
Comment.news_id=1
Comment.hidden=False
Cache Store Criterion
+ Comment
hiden True
news_id 1
Можно индексировать части запросов
63
News.hiden=False
Comment.hidden=False
Cache Store Criterion
Тоже есть готова реализация
http://hackflow.com/blog/2014/03/09/on-orm-
cache-invalidation/
https://github.com/suor/django-cacheops
64
Эффективное кэширование с помощью
cacheops
65
1
2
3
4
5
6
7
8
9
10
def some_view(request, url_param):
items = SomeModel.objects.filter(some_field=url_param)
@cached_view_as(items)
def _render(request, url_param):
c = Context({'items': items})
response = Template("my_template.html").render(c)
return HttpResponse(response)
return _render(request, url_param)
Эффективное кэширование с помощью
cacheops
66
1
2
3
4
5
6
7
8
9
10
def some_view(request, url_param):
items = SomeModel.objects.filter(some_field=url_param)
@cached_view_as(items)
def _render(request, url_param):
c = Context({'items': items})
response = Template("my_template.html").render(c)
return HttpResponse(response)
return _render(request, url_param)
Эффективное кэширование с помощью
cacheops
67
1
2
3
4
5
6
7
8
9
10
def some_view(request, url_param):
items = SomeModel.objects.filter(some_field=url_param)
@cached_view_as(items)
def _render(request, url_param):
c = Context({'items': items})
response = Template("my_template.html").render(c)
return HttpResponse(response)
return _render(request, url_param)
Как же быть с Class Based View?
68
Class Based View
69
1
2
3
4
5
6
7
8
class SomeModelListView(ListView)
def get_queryset(self):
return SomeModel.objects.all()
def get(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
context = self.get_context_data()
return self.render_to_response(context)
Class Based View
70
1
2
3
4
5
6
7
8
class SomeModelListView(ListView)
def get_queryset(self):
return SomeModel.objects.all()
def get(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
context = self.get_context_data()
return self.render_to_response(context)
Class Based View
71
1
2
3
4
5
6
7
class CachedListView(ListView):
def get(self, request, *args, **kwargs):
@cached_view_as(self.get_queryset())
def _get(request, *args, **kwargs):
return = super(CachedListView, self).get(
request, *args, **kwargs)
return _get(request, *args, **kwargs)
Class Based View
72
1
2
3
4
5
6
7
class CachedListView(ListView):
def get(self, request, *args, **kwargs):
@cached_view_as(self.get_queryset())
def _get(request, *args, **kwargs):
return = super(CachedListView, self).get(
request, *args, **kwargs)
return _get(request, *args, **kwargs)
Class Based View
73
1
2
3
4
5
6
7
class CachedListView(ListView):
def get(self, request, *args, **kwargs):
@cached_view_as(self.get_queryset())
def _get(request, *args, **kwargs):
return = super(CachedListView, self).get(
request, *args, **kwargs)
return _get(request, *args, **kwargs)
Class Based View
74
1
2
class NewsView(CachedListView):
model = News
Кэшируйте правильно!
Инвалидируйте правильно!
75
Кэшируйте правильно!
Инвалидируйте правильно!
ir4y.ix@gmail.com
76

More Related Content

PDF
Изоморфные приложения и Python - Виталий Глибин, Huntflow
PPTX
CodeFest 2011. Крестьянинов М. — Обзор аспектно-ориентированного программиров...
PPT
Alexander manuhin selenium_php_v2.0
PPTX
Selenium: начало работы
PDF
WebCamp: Developer Day: Parse'им бэкенд - Аким Халилов
PDF
Контроль качества верстки или как начать делать Makeup
PPTX
Tdd webpack + testem + mocha + chai
PDF
RDSDataSource: Чистые тесты на Swift
Изоморфные приложения и Python - Виталий Глибин, Huntflow
CodeFest 2011. Крестьянинов М. — Обзор аспектно-ориентированного программиров...
Alexander manuhin selenium_php_v2.0
Selenium: начало работы
WebCamp: Developer Day: Parse'им бэкенд - Аким Халилов
Контроль качества верстки или как начать делать Makeup
Tdd webpack + testem + mocha + chai
RDSDataSource: Чистые тесты на Swift

What's hot (20)

PPTX
XPath локаторы в Selenium WebDriver
PDF
"Жизнь без интернета" Кувалдин Артём, Яндекс
PDF
Автоматизация UI тестирования под Windows и Windows Phone
PDF
«Изоморфные js приложения с использованием catberry.js», Денис Речкунов
PPTX
Web осень 2013 лекция 5
PDF
React со скоростью света: не совсем обычный серверный рендеринг
PDF
#2 "Распространённые ошибки в JavaScript" Денис Речкунов
PDF
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
PDF
10 - Web-технологии. MVC фреймворки (продолжение)
PDF
Component Inspector
PPTX
Selenium: приемы работы
PPTX
Selenium vs AJAX
PDF
Jsfwdays 2013-2
PPTX
ZFConf 2011: Разделение труда: Организация многозадачной, распределенной сист...
PPTX
Примеры быстрой разработки API на масштабируемом сервере приложений Impress д...
PDF
Performance optimisation in javascript
PDF
Инструментируй это
PPTX
Коротко о React.js
PDF
Михаил Давыдов — JavaScript: Асинхронность
PDF
FPUG Dzyga presentation
XPath локаторы в Selenium WebDriver
"Жизнь без интернета" Кувалдин Артём, Яндекс
Автоматизация UI тестирования под Windows и Windows Phone
«Изоморфные js приложения с использованием catberry.js», Денис Речкунов
Web осень 2013 лекция 5
React со скоростью света: не совсем обычный серверный рендеринг
#2 "Распространённые ошибки в JavaScript" Денис Речкунов
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
10 - Web-технологии. MVC фреймворки (продолжение)
Component Inspector
Selenium: приемы работы
Selenium vs AJAX
Jsfwdays 2013-2
ZFConf 2011: Разделение труда: Организация многозадачной, распределенной сист...
Примеры быстрой разработки API на масштабируемом сервере приложений Impress д...
Performance optimisation in javascript
Инструментируй это
Коротко о React.js
Михаил Давыдов — JavaScript: Асинхронность
FPUG Dzyga presentation
Ad

Similar to View как чистая функция от состояния базы данных - Илья Беда, bro.agency (20)

PPT
Производительность в Django
PPTX
django-and-postgresql
PPTX
Web осень 2013 лекция 8
PDF
Лекция #7. Django ORM
PDF
13 - Web-технологии. Отображение данных
PDF
Web осень 2013 лекция 6
PDF
SQL. Django, начало
PDF
django cheBit'11
 
PDF
Making of external DSL for Django ORM - Павел Петлинский, Rambler&Co
PDF
Сергей Чистович "Подходы к кешированию на UGC-сервисе"
PPT
Web весна 2013 лекция 4
PPT
Django шахрай. версия 4
PPT
Web осень 2012 лекция 4
PPTX
Web осень 2013 лекция 7
PPT
Введение в Django
PPTX
Django: инструкция по применению
PPT
Самые частые проблемы и пути решения при росте нагрузки и масштабировании про...
PPT
Самые частые проблемы и пути решения при росте нагрузки и масштабировании про...
PDF
GAE - плюсы/минусы/подводные камни
Производительность в Django
django-and-postgresql
Web осень 2013 лекция 8
Лекция #7. Django ORM
13 - Web-технологии. Отображение данных
Web осень 2013 лекция 6
SQL. Django, начало
django cheBit'11
 
Making of external DSL for Django ORM - Павел Петлинский, Rambler&Co
Сергей Чистович "Подходы к кешированию на UGC-сервисе"
Web весна 2013 лекция 4
Django шахрай. версия 4
Web осень 2012 лекция 4
Web осень 2013 лекция 7
Введение в Django
Django: инструкция по применению
Самые частые проблемы и пути решения при росте нагрузки и масштабировании про...
Самые частые проблемы и пути решения при росте нагрузки и масштабировании про...
GAE - плюсы/минусы/подводные камни
Ad

More from it-people (20)

PDF
«Про аналитику и серебряные пули» Александр Подсобляев, Rambler&Co
PDF
«Scrapy internals» Александр Сибиряков, Scrapinghub
PDF
«Отладка в Python 3.6: Быстрее, Выше, Сильнее» Елизавета Шашкова, JetBrains
PDF
«Gevent — быть или не быть?» Александр Мокров, Positive Technologies
PDF
«Ещё один Поиск Яндекса» Александр Кошелев, Яндекс
PDF
«How I Learned to Stop Worrying and Love the BFG: нагрузочное тестирование со...
PDF
«Write once run anywhere — почём опиум для народа?» Игорь Новиков, Scalr
PDF
«Gensim — тематическое моделирование для людей» Иван Меньших, Лев Константино...
PDF
«Тотальный контроль производительности» Михаил Юматов, ЦИАН
PDF
«Детские болезни live-чата» Ольга Сентемова, Тинькофф Банк
PDF
«Микросервисы наносят ответный удар!» Олег Чуркин, Rambler&Co
PDF
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
PDF
«Что такое serverless-архитектура и как с ней жить?» Николай Марков, Aligned ...
PDF
«Python на острие бритвы: PyPy project» Александр Кошкин, Positive Technologies
PDF
«PyWat. А хорошо ли вы знаете Python?» Александр Швец, Marilyn System
PDF
«(Без)опасный Python», Иван Цыганов, Positive Technologies
PDF
«Python of Things», Кирилл Борисов, Яндекс
PDF
«Как сделать так, чтобы тесты на Swift не причиняли боль» Сычев Александр, Ra...
PDF
«Клиенту и серверу нужно поговорить» Прокопов Никита, Cognician
PDF
«Кошелек или деньги: сложный выбор между памятью и процессором» Алексеенко Иг...
«Про аналитику и серебряные пули» Александр Подсобляев, Rambler&Co
«Scrapy internals» Александр Сибиряков, Scrapinghub
«Отладка в Python 3.6: Быстрее, Выше, Сильнее» Елизавета Шашкова, JetBrains
«Gevent — быть или не быть?» Александр Мокров, Positive Technologies
«Ещё один Поиск Яндекса» Александр Кошелев, Яндекс
«How I Learned to Stop Worrying and Love the BFG: нагрузочное тестирование со...
«Write once run anywhere — почём опиум для народа?» Игорь Новиков, Scalr
«Gensim — тематическое моделирование для людей» Иван Меньших, Лев Константино...
«Тотальный контроль производительности» Михаил Юматов, ЦИАН
«Детские болезни live-чата» Ольга Сентемова, Тинькофф Банк
«Микросервисы наносят ответный удар!» Олег Чуркин, Rambler&Co
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
«Что такое serverless-архитектура и как с ней жить?» Николай Марков, Aligned ...
«Python на острие бритвы: PyPy project» Александр Кошкин, Positive Technologies
«PyWat. А хорошо ли вы знаете Python?» Александр Швец, Marilyn System
«(Без)опасный Python», Иван Цыганов, Positive Technologies
«Python of Things», Кирилл Борисов, Яндекс
«Как сделать так, чтобы тесты на Swift не причиняли боль» Сычев Александр, Ra...
«Клиенту и серверу нужно поговорить» Прокопов Никита, Cognician
«Кошелек или деньги: сложный выбор между памятью и процессором» Алексеенко Иг...

View как чистая функция от состояния базы данных - Илья Беда, bro.agency