На прошлой неделе я написал небольшой скрипт в 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 действительно не может передавать данные из наборов при их чтении.
В конце концов алгоритм для получения перфораторов работал так:
- Читать объект из Монго
- Установите флажок Redis, если объект уже обработан
- Получить Punchard
- Магазин перфокарт
- Добавить нового участника в Redis set маркировка, мы сделали это
Из-за некоторых несоответствий в ответах Github в первый раз, когда я запустил это, он потерпел крах через 5 минут после того, как я заснул.
Следующий пробег прошел намного лучше!
За исключением того, что экземпляру не хватило места на диске после обработки некоторых репозиториев 110 КБ, и я заметил только после ожидания 14 часов для обработки всего. Дети, распечатайте свои ошибки!
Третий раз — очарование, и в понедельник утром у меня были все перфокарты в базе данных Монго примерно на 6 гигабайт. Всего 504,015 из них.
К счастью, я потратил предыдущий день на написание сценария, вычисляющего гистограммы — уходит около 3 минут, чтобы перебрать набор данных — и поиграть с d3.js, чтобы он выглядел хорошо.
Всего набор данных содержит 164 509 270 коммитов . Пока только в форме перфокарты. Это означает, что у меня есть только сетка 7 × 24, в которой указано, сколько коммитов произошло за это время.
Следующий шаг — найти способ различать хобби и рабочие проекты, чтобы увидеть, влияет ли это на распределение времени коммитов. Пожелай мне удачи.
PS: Если вы хотите набор данных, отправьте мне письмо, и мы можем что-то организовать
PPS: для тех, кто заинтересован, весь код для извлечения моего набора данных находится на github