Как слепить голосовалку «на коленке» за пару часов?

Недавно окунулся в мир serverless приложений − нужно было по быстрому слепить простую анонимную голосовалку, желательно с разумным ограничением количества голосов от одного юзера, при этом чтобы все составляющие стека были бесплатными для использования.

СОВЕТ
Всегда внимательно изучайте лимиты бесплатных сервисов (они есть всегда!).

Времени на бэкенд не было, аппка нужна вчера. Поэтому стэк бека изначально был такой:

  • Firestore — будет хранить выбранные юзером варианты
  • Firebase storage — будет хранить картинки для вариантов ответов
  • Google spreadsheet — станет удобным представлением данных для менеджера
  • Zapier — будет выполнять синхронизацию firebase и spreadsheet

На фронте юзается Vue+Vuex, клиентская либа firebase и fingerprint2 для хоть какого-то ограничения юзеров (по факту, нельзя голосовать с одного браузера дважды, причем либа умеет учитывать режим инкогнито).

База данных Firestore не представляет из себя ничего заумного − это просто сторэдж для форматированных данных вида key->{object}, object — набор ответов пользователя, да таймстамп.

Кроме того, поскольку приложение подразумевает ТОЛЬКО сбор и чтение данных, было не лишним запретить удаление/изменение данных:

Варианты ответов решено было поставлять на клиент сразу с кодом приложения, а не хранить в БД — это позволило и сократить время разработки и сэкономить на запросах к Firestore.

Все варианты ответов сопровождались фотографиями, поэтому изначально было решено запихать их в удобный firebase storage, что было ошибкой — оказалось что на него действуют собственные квоты, а не те, что описаны для firebase.

Видите «View quota»? Вот и я не сразу её увидел

Короче, как оказалось, лимит у сторейджа − 1.5Gb, который израсходовался в первый же день, и пришлось искать альтернативные пути (спойлер: dropbox).

Последнее, что нужно для выдачи продукта клиенту — возможность отслеживать ситуацию. Изначально, я размышлял о двух возможных вариантах:

  1. Накидать простой скрипт, что будет строить отчет о состоянии данных в FB
  2. Накидать простой сервис, единственная задача которого заключалась бы в визуализации данных

В итоге, от первого отказался, поскольку это не user-friendly (а сам я разумеется не хочу по запросу запускать скрипт и скидывать инфу), от второго отказался, поскольку вспомнил о существовании готовых средств визуализации данных, а именно Google Spreadsheets (или по народному − эксель от гугл)

Самый простой и быстрый способ подружить Spreadsheets и Firebase мне виделся в использовании сервиса Zapier.

В итоге накидал такую простенькую структуру документа:

Это представление данных, источник в соседнем sheet’е заполняется из данных firebase

Чтобы автоматизировать выгрузку данных firebase -> spreadsheet, накидал элементарный zap:

Там в настройках всё элементарно, но работает эта связка коряво

Но вот беда — костыль с zapier не проканал по двум причинам:

  1. Почему-то он пропускал некоторые новые записи в базе, несмотря на наличие специального timestamp поля
  2. 100 запусков на бесплатной версии — это даже для такого маленького проекта «ни о чём»

К моменту плясок с zapier, приложение уже вовсю собирало голоса (причем это был пиковый момент жизни приложения)

поэтому нужно было срочно что-то решать с выгрузкой данных в эксель.

СОВЕТ
Всегда используйте самые простые и очевидные инструменты

Простым и бесплатным решением проблемы стал GScript, приаттаченный к таблице:

GS — это всё тот же JS, запилить на нём интеграцию — как раз плюнуть.

К сожалению, Google не предоставляют нативного доступа к Firestore, поэтому пришлось установить дополнительную либу:

Для обновления данных в таблице подразумевалось наличие кнопки, при клике на которую, данные подсасывались бы из firestore.

СОВЕТ
Лучший UX даёт интерфейс, которого вы не видите

Проблемность такого решения, помимо очевидного, заключается также в следующем:

  1. Пользователя нужно обязательно добавлять в список доступа (иначе он не сможет запустить GScript)
  2. Пользователь должен обладать правами на запись в таблице-источнике (этого хотелось избежать изначально)

Решение − настроить триггеры для проекта в интерфейсе script.google.com:

Обновление данных «раз в 30 минут» видится мне наиболее приемлемым вариантом, поскольку это всего лишь 48 запросов в сутки, и данные всегда достаточно свежие.

Тем более, таким образом мы избегаем возникновение коллизий — если запросы выполняются часто, то при большой нагруженности сервиса есть риск получить мешанину данных, когда один скрипт не успел закончить свою катку, а уже запустился следующий (в случае этого приложения, из firestore берутся все записи старше последней даты в таблице-источнике).

СОВЕТ
Никогда не делайте преждевременную оптимизацию, особенно если требования к проекту не до конца ясны.

Разумеется, можно лимитировать запросы, но это лишние запросы к базе firestore (у нас ведь есть лимиты, помните?)

Кроме того, никогда не рассчитывайте на клиентское время при разработке, иначе можете запросто получить «интересные эффекты». К сожалению, в firestore нет аналога триггеров SQL, чтобы сделать автоматическое создание поля, поэтому пришлось запиливать это лишним API запросом:

Это позволит сохранить консистентность дат, что поможет вам избежать проблем от пользователей с другими временными зонами и прочим.

Теперь можно спокойно передавать таблицу кому угодно — либо давать доступ по ссылке, либо добавлять конкретных юзеров в проект (не забудьте при этом ограничить остальным пользователям доступ на запись в таблицу-источник).

comments powered by Disqus