Статьи

Испытания и невзгоды большого набора данных

На прошлой неделе я написал небольшой скрипт в node.js.

Его цель? ПОЛУЧИТЕ ВСЕ ДАННЫЕ!

Фиксируйте гистограмму времени

План состоял в том, чтобы очистить Github от массивного набора данных и провести некоторый анализ рабочих привычек программистов, по крайней мере тех, которые используют Github. Таким образом, скрипт был довольно прост — запустил поисковый запрос через API, получил страницы 1, 2, 3…

При 100 репозиториях на страницу с возвратом в несколько секунд и ожиданием в течение часа, прежде чем им будет разрешено делать больше запросов после каждых нескольких сотен страниц, работа по очистке заняла большую часть недели. Иногда сценарий зависал без  ошибки 403, поэтому я изменил запрос. Это исправило это.

Иногда это могло рухнуть — сразу после того, как я пошла спать, конечно.

В субботу утром у меня была база данных mongo со списком из  513 900 репозиториев на небольшом экземпляре EC2. Они не были гарантированы, чтобы быть уникальными.

Когда N становится проблемой

Следующим шагом был просмотр списка хранилищ и выборка данных перфокарт для каждого.

Это перфокарта

Это перфокарта

Основная поговорка о практической информатике звучит примерно так:  «Наш алгоритм O (N ^ 5) отстой для большого N, но обычно N меньше 10»

Хотя в большинстве случаев это не очень большой набор данных, полмиллиона, этого было достаточно, чтобы помешать моему первоначальному подходу использования Mongoose для обработки моего соединения с mongodb.

Хотя Mongoose отлично подходил для написания, он не смог полностью прочитать. Не совсем точно, где была проблема, но запуск такого рода кода быстро заставил мой экземпляр EC2 исчерпать память:

models.repo.find({}, function (err, repos) {
   repos.forEach(do_something);
});

Где  models.repo  — это Схема для хранилища.

Поэтому я был вынужден отказаться от относительного комфорта, предоставляемого Mongoose, и прочитать мои данные по старинке:

mongo.Db.connect(
        format("mongodb://%s:%s/github-nightowls?w=1", 'localhost', 27017),
        function(err, _db) {
 
            var repos = _db.collection('repos'),
                punchcards = _db.collection('punchcards');
 
            repos.find().each(function (err, repo) {
// ...

Намного больше работы, но это даже не показывает, как использование памяти. Я предполагаю, что Mongoose поступал неправильно и не передавал данные прямо из базы данных в мою функцию обратного вызова , но пытался сохранить все это в памяти. Глупый мангуст.

Был еще один шаг! Гарантируя, что я получаю данные перфокарты только для уникальных репозиториев.

Моей первой попыткой было просмотреть данные, добавить комбинацию имени пользователя / имени каждого репозитория в набор redis, а затем следовать этому набору при получении перфокарт. Помните, наборы гарантируют, что каждый участник появляется только один раз.

Увы. После запуска сценария в течение нескольких минут — о, да, я уже говорил, что на чтение всего списка репозиториев уходит около 3 минут? — Оказывается, набор Redis был настолько масштабным, что мои попытки его чтения оказались безрезультатными. Не хватило памяти, прежде чем делать что-то серьезное.

На этот раз я не уверен, что это ошибка node_redis , или Redis действительно не может передавать данные из наборов при их чтении.

В конце концов алгоритм для получения перфораторов работал так:

  1. Читать объект из Монго
  2. Установите флажок Redis, если объект уже обработан
  3. Получить Punchard
  4. Магазин перфокарт
  5. Добавить нового участника в Redis set маркировка, мы сделали это

Из-за некоторых несоответствий в ответах Github в первый раз, когда я запустил это, он потерпел крах через 5 минут после того, как я заснул.

Следующий пробег прошел намного лучше!

За исключением того, что экземпляру не хватило места на диске после обработки некоторых репозиториев 110 КБ, и я заметил только после ожидания 14 часов для обработки всего. Дети, распечатайте свои ошибки!

Третий раз — очарование, и в понедельник утром у меня были все перфокарты в базе данных Монго примерно на 6 гигабайт. Всего  504,015  из них.

К счастью, я потратил предыдущий день на написание сценария, вычисляющего гистограммы — уходит около  3 минут, чтобы перебрать набор данных — и поиграть с d3.js, чтобы он выглядел хорошо.

Всего набор данных содержит  164 509 270 коммитов  . Пока только в форме перфокарты. Это означает, что у меня есть только сетка 7 × 24, в которой указано, сколько коммитов произошло за это время.

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

PS: Если вы хотите набор данных, отправьте мне письмо, и мы можем что-то организовать

PPS: для тех, кто заинтересован, весь код для извлечения моего набора данных находится на github