PersistJS и TaffyDB. Как поселить почти настоящую базу данных в браузер. Часть 1

June 1, 2009

Хм, база данных в браузере, да еще и “почти настоящая”, как это и, самое главное, кому это нужно? Не было бы ничего более простого, чем начать эту статью со стандартных фраз, вроде следующей: “За последние годы все более и более выраженной становится тенденция наделения веб-приложений, чертами своих старших “настольных” собратьев”. И под этим “соусом” обосновать необходимость для веб-приложения хранить данные не только на веб-сервере, но и на локальном компьютере пользователя. Затем можно быстро перейти к “немножко попрограммировать”, завершив статью рассказом о паре замечательных javascript-библиотек, вынесенных в заголовок (PersistJS и TaffyDB). Увы, все не так просто и вопрос стоит не столько “о той самой базе данных”, но и интересных направлениях в разработке ПО.

Все началось с того, что пару месяцев назад я задумался об обновлении своего ноутбука. Верно прослуживший мне более пяти лет, агрегат нуждался в замене не столько по причине своего морального устаревания, а, прежде всего, из-за элементарного износа: падения, слабо держащие заряд батареи и пара царапин на экране и, чего уж греха таить, желание приобрести побольше мегабайт оперативной памяти. Все это заставило меня просмотреть рекламу нескольких компьютерных магазинов. Посмотрел и понял, что еще морально не готов платить, минимум, 700-800 единичек за современный и производительный ноутбук. Да, я могу запустить и комфортно работать в больших приложениях, вроде того же photoshop, но фокус в том, что мне это не нужно: 90% моих потребностей покрываются office или веб-браузером. Ну разве что, изредка запущу eclipse или intellij idea. Затем мой взгляд обратился в сторону, будоражащих последний год рынок, нетбуков или дешевых нотбуков. Технически они представляют собой ноутбуки с производительностью актуальной года три назад, но за совсем небольшие деньги (порядка 300 единичек) и на новой технологической базе: беспроводные коммуникации и хранение данных на основе SSD – все это очень привлекательно. Если бы не маленький размер экрана и возможные сложности привыкания к не самой большой клавиатуре, то я вполне купил бы себе именно нетбук. Пока же я решил вопрос об обновлении “железа” отложить, но осталась мысль. Почему, изначально задумывавшиеся как устройства для развивающихся рынков, нетбуки за всего год заняли почти 10% мирового производства ноутбуков и их покупатели в основном не “голодные африканские дети”, а “пузатые буржуа” из Европы или северной Америки и как это может повлиять на рынок разработки ПО? На днях мне под настроение попалась статья http://habrahabr.ru/blogs/netbook/59562/, также рассказывающая о причинах роста популярности нетбуков. И я задумался над тем, а может быть это и есть начало долгожданной революции и переноса приложений из настольного формата в веб-формат. Об этом давно мечтают разработчики программного обеспечения: новые способы зарабатывать деньги, связанные с арендной платой за пользование программами, онлайн-рекламой легли на технологический рост в виде все более широкого распространения (да же в наших пенатах) беспроводного Интернет и развитие технологии облачных вычислений. Все это дает возможность сэкономить на печати дисков, логистике, рекламе и маркетинге. Облачные вычисления дают возможность арендовать сервера для размещения веб-приложений максимально гибко: не нужно вкладывать “константные” миллионы на покупку сразу большого количества серверов (а вдруг проект выстрелит не сразу, а через какое-то время). В можно варьировать (не только добирая, но и отдавая) объем покупаемых вычислительных мощностей чуть ли не каждый день. Все это, если, конечно, у вас есть хорошая идея и бизнес-план как выжать из нее максимум денег, дает возможность выйти на рынок не только большим и богатым компаниям, но и небольшим коллективам и даже индивидуальным разработчикам. Конечно, и раньше рынок не принадлежал целиком только “большим” компаниям и те же индивидуальные разработчики могли заработать неплохие деньги, разрабатывая небольшие shareware-программки или игры и ведя адресные продажи через internet. Фокус в другом: я только что специально пересмотрел список программ, размещенных у меня на компьютере в “Program Files”, и обнаружил довольно большое количество мелких утилит (в большинстве своем или shareware или freeware), которые мне были нужны мне когда-то. Эти утилиты я скачивал из internet, тестировал, выбирал самые лучшие, а вот теперь они пылятся без дела – вместо них я нашел аналоги в виде веб-приложений. Дошло до того, что после последней переустановки компьютера я до сих пор не установил себе the bat!, а пользуюсь gmail. Еще я не инсталлировал lingvo (вот хорошо, что вспомнил, сейчас же займусь установкой), а пользуюсь google translate и еще несколько подобных примеров. И я совсем не уверен, что если мне завтра для работы потребуется какая-нибудь небольшая утилитка, то я буду искать ее среди развалов каталогов shareware/freeware программ. Затем устанавливать (я вдруг в ходе инсталляции, что-то пойдет не так). Потом удалять приложение, если не понравится и, наконец, проверять обновления. Мне проще открыть окно браузера и все. Кто знает, возможно, через несколько лет я (ну хорошо, не я, а массовый пользователь, потребности которого сводятся к “tut.by”, “одноклассникам” и немножко word) буду пользоваться только одной программой – internet браузером и только. Конечно, все это применимо только в случае стран с развитым и дешевым internet-ом. Но, ведь какое дело, говорят, что за границей телекоммуникационные провайдеры дошли до того, что начали бесплатно раздавать те же нетбуки всем, кто купил тарифный план на пару лет.

Все это здорово, но если ранее пользователь мог бояться потерять несохраненные данные, только в случае “зависания компьютера”, то перенос части приложений в веб-формат добавляет ряд проблем связанных с “тем, что вовне”. Раньше пользователь, обладая ноутбуком или наладонником, мог работать над своими документами где и когда угодно. Но, придя в офис или домой, должен был выполнить синхронизацию файлов, перенеся их в корпоративную сеть или сохранив на рабочий стол компьютера. Вскоре этот шаг будет не нужным: когда вы едете в метро и редактируете документ google docs, то этот документ сразу будет доступен для просмотра вашим коллегам. Все это звучит очень стильно, но в практике бывают ситуации, когда нельзя полностью перенести хранение данных на сервера в internet: ведь никто не отменял форс-мажорных обстоятельств, когда из-за аппаратного сбоя сервера, линии связи или атаки хакеров тот же сайт google docs перестанет откликаться. Т.е. обойтись только одной “волшебной” кнопкой, сохраняющей файл на сервер, не получится – нужны две кнопки. Первая кнопка будет сохранять ваши документы на сервер, но если эта операция будет не доступна, то правки должны быть сохранены в вашем компьютере. Затем после того как связь будет восстановлена, то вторая кнопка выполнит синхронизацию локальных изменений с теми, что в internet.

Как вывод, для того, чтобы добиться успеху любому веб-приложению, предназначенному для не только просмотра, но и редактирования информации мало удобного и красивого интерфейса пользователя. К слову, недавно я завершил серию статей про одну из таких “тяжелых” и красивых библиотек построения пользовательских интерфейсов - Yahoo UI. Другое проявление “роста” веб-приложений – это появление возможности работать без подключения к internet. Т.е. наше веб-приложение не только умеет при запуске загружать весь объем данных нужных для работы из internet, но и умеет сохранять копию данных на компьютере пользователя. Сегодня я расскажу об двух javascript-библиотеках помогающих пользоваться внутри веб-приложения хотя бы частью тех возможностей, которые есть в настольных СУБД. В январе 2008 года я написал серию статей, рассказывающих об идеях и перспективах хранения сложных данных на стороне клиента и прикладном инструменте google gears (на самом деле gears – это много больше, чем просто браузерная СУБД). К сожалению, “выстрела” не получилось: прошло почти полтора года, и google gears применяется только на небольшом проценте веб-сайтов. Причина оказалась не в самой технологии или косности мышления веб-мастеров – а в том, что очень небольшой процент “домохозяек” будет устанавливать у себя на компьютере какой-то плагин. Внедрение google gears в, выпущенный google, браузер chrome не оказало никакого влияния на рынок, т.к. тот же chrome остался браузером для гиков, а лидирующие позиции на рынке остались за internet explorer 6 и 7. В последние несколько месяцев ситуация изменилась: самое главное то, что microsoft выпустил восьмую версию internet explorer, и впервые за много лет обратил внимание на соблюдение веб-стандартов и даже на перспективный html 5. Что еще важно, так это включение internet 8 в список критических обновлений windows. Это значит, что обновление браузера будет скачиваться и инсталлироваться автоматически, а значит, следует ожидать массовой миграции пользователей с 6 и 7-ой версии IE на восьмерку. Среди множества улучшений в 8-ой версии IE я считаю одним из важнейших поддержку технологии domStorages (давно поддерживаемой firefox и игнорируемую opera). domStorages как раз и предназначена для сохранения на стороне клиента информации между перезапусками браузера. Но прежде чем я начну рассказ о практическом применении domStorages, стоит сказать пару слов о том, с чего все началось. Представление о том, что такое база данных и что она должна уметь делать варьируется у различных категорий программистов и уж тем более отличается от того, что вкладывают в слово СУБД разработчики и администраторы таких монструозных продуктов как microsoft sql server или oracle. Я предъявляю к браузерной СУБД только два требования. Во-первых, возможность сохранять информацию между перезапусками браузера или компьютера. А во-вторых, наличие хоть каких-то средств для хранения сложных структур данных (массивы и объекты), и средств поиска среди этой информации. К сожалению, в комплекте обе эти возможности представлены только в google gears, или в adobe air (но ведь это тоже плагин и требует отдельной установки). С другой стороны, сейчас есть несколько javascript-библиотек, позволяющих в отдельности либо организовать хранение информации, либо рассматривать массив объектов как какое-то подобие таблицы БД и выполнять над ней поисковые запросы и запросы на модификацию данных. Сегодняшняя статья будет посвящена вопросам сохранения данных между перезапусками браузера (библиотека persistJs), а вторая статья серии раскроет методы поиска и изменения записей (taffy). Но сначала немного истории.

С самых первых дней развития internet был предусмотрен инструмент (cookie), с помощью которого веб-сайт мог сохранить в браузере клиента небольшой кусочек не сложной информации. Ключевые слова здесь – “не большой” и “не сложной”. Использование cookies для хранения чего-то большего, чем пара строк текста (а мы ведь планировали сохранить целую электронную таблицу google spreadsheet) была не возможна по двум причинам. Во-первых, ограничение по максимальному размеру сохраняемой информации в 4 килобайта, плюс ограничение по количеству элементов, хранимых в cookie (это число зависело от браузера и плавало в диапазоне 20-50 элементов). И, что особо неприятно, информация, хранящаяся в cookie, посылалась на сервер при выполнении каждого запроса, хоть за html-страницей, хоть за javascript-файлом или картинкой. Как вывод, cookie следует использовать только для хранения сессий (значение cookie может установить и серверный скрипт), а для хранения данных нам нужна “почти настоящая база данных”. Альтернативным способом хранения “больших данных” стало использование flash: разработчики adobe предусмотрели для flash¬ролика функцию сохранения информации на машине клиента (объект SharedObject). Таким образом, появился подход, когда html-страница, нуждающаяся в сохранении информации, передавала ее внутрь flash-ролика (для организации взаимодействия между flash и javascript использовался ExternalInterface). И, естественным было рождение нескольких javascript-библиотечек, которые автоматически внедряли в код html-страницы flash-ролик (ролик делался нулевого размера, чтобы быть не заметным) и представляли ряд функций, вроде save, get, delete, которые просто перевызывали соответствующие методы из flash-ролика. В те времена, когда господство internet explorer в internet было неоспоримым, а flash не был широко распространен, популярность приобрела еще одна методика сохранения данных, специфическая только для ie, - userData behavior. И как следствие появились javascript-библиотеки, которые прятали различные методики хранения данных за унифицированным набором функций. Тема сегодняшнего материала – persistjs представляет собой такую обертку над 7-ю различными стратегиями сохранения данных (и все эти возможности втиснуты в файл размером менее 10 килобайт). Вот список поддерживаемых технологий: 'gears', 'localstorage', 'whatwg_db', 'globalstorage', 'flash', 'ie', 'cookie'. При запуске persistjs последовательно перебирает (в порядке совпадающим с указанным выше) возможные методики до тех пока одна из них не подойдет. В случае, если в браузере инсталлирован плагин google gears, то данные будут сохранены с его помощью. Впоследствии вы можете управлять настройками gears, запрещая или разрешая для определенных сайтов использование gears хранилища. Технически, внутри gears бьется “пламенное сердце” sqlite – базы данных, популярной, производительной и с большим набором функций. А значит, вы можете хранить на машине клиента большие объемы данных, строить сложные поисковые запросы, использовать JOIN-объединения. Сможете работать с подзапросами, как в секции where, так и в секции from и даже значения отдельных колонок могут быть вычислены с помощью подзапроса. Подробнее о sqlite можно узнать во второй статье моей серии про google gears или на сайте проекта http://www.sqlite.org/. К слову сказать, одна из перечисленных в списке выше технологий хранения данных (whatwg_db) также полагается на хранение данных в виде базы sqlite. Но важно понять, что хотя sqlite – настоящая СУБД (т.е. данные хранятся в виде таблиц), но остальные методики вовсе не представляют такой функциональности и часто ограничены возможностью сохранить только “плоскую” строку текста. Таким образом, если вам нужна максимальная производительность и гибкость в поисковых запросах вы не сможете использовать “наименьший общий знаменатель” persistjs, а должны напрямую работать с google gears. Вторая технология – localstorage – это стандартный (объявлен в стандарте html 5) способ для хранения не сложных данных в браузере (опять таки, данные должны быть представлены строкой текста). Появление стандарта html5 было долгим и мучительным, в ходе чего некоторые из идей (уже реализованные) менялись и становились не рекомендуемыми. По состоянию на текущий момент браузер с поддержкой html5 должен предоставлять следующие средства для хранения информации. Технологии localStorage и sessionStorage (вместе они называются domStorage) предназначены для хранения данных в виде простых пар “ключ” и “значение”. Вот простой пример использования localStorage:
  1. var x = localStorage['x'];
  2. if (x)
  3.   alert ('read ' + x);
  4. else{
  5.   localStorage['x'] = x = 'current timestamp is '+ new Date ();
  6.   alert (‘saving ’+ x); 
  7. }
Этот пример кода отлично работает в IE 8 и демонстрирует прием, когда при первом посещении страницы проверяется наличие внутри localStorage переменной с именем “x” и если она есть, то выводится. В случае, когда это не так, то внутрь localStorage записывается текущая дата. Попробуйте выполнить этот пример, попробуйте клавишу “f5” для того, чтобы перезагрузить страницу, закройте и откройте браузер заново. И вы убедитесь в том, что информация в localStorage сохраняется до тех пор, пока вы ее не удалите. Вторая составляющая domStorage – sessionStorage служит для хранения данных только на время сеанса. Казалось бы, что в таком виде sessionStorage бесполезна и мало отлична от обычной глобальной переменной. Все, на самом деле, зависит от того, что вкладывать в понятие сессии. Здесь подразумевается все время, в течении которого пользователь работает с сайтом, пока явно не закроет веб-страницу. А значит, что нажатие кнопки “f5” не приводит к завершению сессии и к потере сохраненных данных (состояние обычных переменных будет утеряно). Также если браузер зависнет, но после перезапуска предложит восстановить сессию, то будет восстановлен не только набор открытых закладок, но и данные, которые были сохранены в sessionStorage. А вот закрытие окна браузера и открытие его заново означает нормальное завершение сессии и данные из sessionStorage будут очищены. Браузер mozilla firefox 3.0 не поддерживает localStorage (зато есть поддержка sessionStorage), вместо этого есть поддержка globalStorage – технологии похожей на localStorage, предлагавшейся в раннем варианте html5, но не “прошедшей в финал”. Поддержка globalStorage впервые появилась, начиная с версии firefox 2.0 и использование ее очень похоже на работу с localStorage:
  1. var storage = globalStorage[location.hostname];
  2. var x = storage['x'];
  3. if (x)
  4.   alert ('read ' + x);
  5. else{
  6.   storage['x'] = x = 'current timestamp is '+ new Date ();
  7.   alert (x); 
  8. }
Единственное отличие только в том, что нельзя информацию непосредственно помещать внутрь globalStorage (фактически globalStorage – это список контейнеров с данными). Вместо этого первым шагом globalStorage берется контейнер, зарезервированный для хранения информации для нашего сайта (имя его домена хранится внутри location.hostname). Если заменить “location.hostname” на произвольную строку, то firefox сообщит о нарушении политики безопасности. Для того, чтобы удалять информацию из хранилища, не важно localStorage или globalStorage, используется метод deleteItem с единственным параметром – именем переменной, которую нужно удалить:
  1. var storage = globalStorage[location.hostname];
  2. storage.removeItem ('x');
  3. // или так
  4. localStorage.removeItem ('x')