Как-то давно мы задумали написать один проект для Node.js которому очень нужно было работать с NoSQL базой данных, но при этом не иметь никаких зависимостей от внешних приложений. Как это обычно бывает, все закончилось разработкой новой библиотеки.

Начав разработку два года назад, желание использовать встроенную базу данных для web приложения казалось весьма странным. В самом деле, зачем? Сейчас, когда появился проект node-webkit, объяснить это гораздо легче. Используя встроенную базу данных возможно разработать web приложение двойного назначения. Такое приложение сможет работать как в классической схеме клиент-сервер, так и с использованием node-webkit как обычное загружаемое приложение. Важной особенностью и в том и другом случае является то, что код базы данных является частью вашего приложения, что избавляет от многих проблем совместимости и установки.

Осмотревшись вокруг, мы нашли несколько различных проектов, но остановились почему-то на Alfred.js. Внешне, да и по первым тестам, он выглядел очень здорово и делал то, что нам нужно. Во-первых, он не загружал все данные в память, а использовал файловый доступ. То есть потенциально он мог работать с относительно большим объемом данных, разумно потребляя память. Во-вторых, он умел использовать индексы, что очень важно. Ну и в третьих, казалось, что он очень хорошо проработан хотя бы потому, что там была даже предусмотрена репликация.

Пройдя экватор в разработке, мы начали спотыкаться об Alfred.js. Реалистичные наборы данных вызывали множественные ошибки включая порчу данных. В какой то момент вместо разработки приложения мы занялись починкой кода базы данных. Потратив много времени с переменным результатом, мы не добились ничего кроме разочарования. Самое главное, что проблемы с базой данных полностью остановили разработку самого приложения, и это расстраивало больше всего.

В какой-то момент времени было принято решение заменить базу данных на MongoDB и одновременно попробовать разработать собственную библиотеку баз данных, которую можно было бы использовать не меняя ничего в коде приложения. То есть такая база данных должна иметь функциональность и API идентичный драйверу MongoDB для Node.js. Было изначально понятно, что сложно реализовать всю функциональность MongoDB. Однако мы точно знали, что все и не нужно. По известному правилу нам нужно только 20% возможностей, которые покроют 80% потребностей. Таким образом, дальнейшая разработка пошла в двух направлениях. Часть команды занялась адаптацией приложения на MongoDB, другая часть разработкой базы данных.

Как ни странно, адаптация кода под MongoDB оказалась не такой тривиальной. Тот факт, что Alfred.js использует похожий синтаксис запросов и методы скорее навредил, чем помог. Этот опыт помог нам уяснить, что только полная идентичность — это путь, по которому нужно двигаться. Поэтому изначально было решено, что все тесты должны работать один в один как с нашей новой базой, данных так и с MongoDB.

Позднее, реализовав базовый функционал, появилась идея взять готовые тесты из проекта Node.js драйвера для MongoDB. Это был серьезный вызов, который мы приняли и осуществили. Интегрируя готовые тесты мы узнали много нового о самом драйвере и где-то даже разочаровались. Заимствованные тесты в большинстве случаев не проверяют коды ошибок, что очень сильно мешало. Кроме прочего, API оказался через чур гибким на наш взгляд. Например, функция Collection.find допускает 7 различных комбинаций параметров и каждый раз, когда ее вызывают, десятки строчек кода занимаются только тем, что пытаются угадать, какой именно вариант был использован. Тем не менее, все это поведение было детально воспроизведено вплоть до текста исключений и вероятных ошибок.

В какой-то момент обоим направлением разработки суждено было встретиться. С одной стороны, мы уже имели хорошо работающее приложение, использующее MongoDB. С другой стороны, мы получили базу данных, которая реализует требующийся нам функционал и проходит несколько сотен тестов. Встреча не прошла идеально, но вскрывшиеся проблемы оказались незначительными. Вскоре приложение заработало со встроенной базой данных так же хорошо, как и с MongoDB, что и продолжает делать до сих пор.

Нашу базу данных мы назвали TingoDB (TInymoNGODB) и опубликовали исходный код на GitHub.

Комментарии (16)


  1. crmMaster
    27.07.2015 13:46
    +3

    Насколько понятна ваша мотивация — вы создали базу данных, которая объективно по производительности и функциональности хуже для того, чтобы она взаимодействовала с библиотекой так, как вам этого хочется?

    { макро с картинкой про троллейбус }


    1. kemsky
      27.07.2015 21:26
      -2

      Такие базы обычно используют в тестах.


      1. crmMaster
        27.07.2015 22:17
        +3

        Если вы используете в тестах окружение, отличное от продакшена, то смысла в таких тестах нет совсем.


        1. kemsky
          28.07.2015 15:31

          Мир не идеален, так ведь и моками нельзя пользоватся, это ж отличное от продакшена. Используют HSQLDB, H2, веб проверяют на phantom.js и тп.


    1. sergeyksv Автор
      29.07.2015 18:03

      Основная мотивация это встраиваемая в процесс nosql база данных не требующая ничего кроме JavaScript. Т.е. отсуствие зависимости от внешних (часто тяжелых) сервисов/библиотек. В этом смысле даже sqlite можно назвать тяжелой зависимостью для JavaScript поскольку она является нативной библиотекой.

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

      Полная совместимость с MongoDB по сути была вторична и не являлась основной целью. Так скажем решили просто не рисковать. В конечном итоге правда это помогло обзавестись несколькими сотнями тестова «за так», просто взяв из проекта нативного драйвера. Сразу кстати скажу есть другие библиотеки которые пишут что они реализуют MongoDB API ( github.com/mWater/minimongo, github.com/louischatriot/nedb). Это не так, они далеки от этого. Наша совместима настолько что «легким движением руки» ее удалось подсунуть в mongoosejs.com.

      Что касается производительности, для объема данных который разумен именно для встраиваемой базы данных он вполне на уровне. Кроме прочего это для сервера важно 5 или 10 мс занял запрос. Для десктоп приложения (например собраного на nwjs.io) это фактически без разницы. С другой стороны ставить полноценную MongDB на дексктоп в довесок к небольшому приложению как бы перебор. Ну и например для малюток типа raspberry pi настоящая mongodb может быть тяжеловата.

      Как то так. Заранее извиняюсь за поздний ответ, пост проболтался в песочнице больше полутора лет и о том что его опубликовали я узнал случайно.


  1. DoctorChaos
    27.07.2015 13:52

    [удалено мной, надо внимательнее читать]


  1. Rayzor
    27.07.2015 14:16

    Кажется, minimongo это что-то в эту сторону.


  1. Jabher
    27.07.2015 14:55
    +5

    Я бы чуть иначе написал.
    Как обычно бывает, нам захотелось сделать модно и на NoSQL
    Вот так лучше. В обычной ситуации люди не заморачиваются и поднимают SQLite :)


    1. sergeyksv Автор
      29.07.2015 18:08

      SQLite не есть NoSQL база данных, это во-первых. Во-вторых это бинарная зависимость (нативная библиотека) и мы очень хотели не иметь таких зависимостей.

      Ну и к слову, NoSQL это не модно, а удобно, в особенности в JavaScript. Ну так для простоты просто получается прямая цепочка передачи JSON (Бразуер — Сервер — База). Но о вкусах как говорится не спорят, кому то нравится SQL + ORM.


  1. radist2s
    27.07.2015 15:27

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


    1. sergeyksv Автор
      29.07.2015 18:17

      Данные мы храним похожим образом, в текстовых небинарных файлах. Перед тем как остановится на этом мы перепробовали и оттестировали несколько вариантов, и именно такой оказался наиболее быстрым. Звучит странно, но JSON это нативный способ сериализации для JavaScript и очевидно допиленный до совершенства.

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

      Вот что важно, данные все в памяти не хранятся, только индексы и кэш. Кроме прочего github.com/louischatriot/nedb API только похож на MongoDB но не совместим с ним не разу. Наша база подсовывается в mongoosejs.com и он этого не замечает.


  1. matiouchkine
    27.07.2015 15:38
    +1

    rethinkdb.com


    1. gbezyuk
      27.07.2015 17:26
      +1

      firebase. тысячи их, это модно.


  1. gbezyuk
    27.07.2015 17:26
    +4

    Не вполне понятно, почему «хранение данных в памяти» считается недостатком. Хассо Платтнер* уже давно показал, что с текущим соотношением быстродействия оперативки и накопителей и их возможностями масштабирования — данные в памяти хранить не только можно, но и нужно; а на дисках — по сути дела бэкапы. *(see open.hpi.de/courses/imdb2015 for details)


    1. aleks_raiden
      27.07.2015 20:16
      -2

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


      1. mukizu
        28.07.2015 12:02

        Ну, вы вполне можете воткнуть очень много памяти и маленький диск, так что технически это не обязательно так)

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