Skip to content

Instantly share code, notes, and snippets.

@korel-san
Last active February 12, 2018 11:36
Show Gist options
  • Save korel-san/20bcabe2d032642483a8e86164a33a1d to your computer and use it in GitHub Desktop.
Save korel-san/20bcabe2d032642483a8e86164a33a1d to your computer and use it in GitHub Desktop.
Detecting and process changes in csv files

Этот пункт скорее всего пропущу, слишком много текста не по делу

Начну с истории задачи, откуда она возникла: Сейчас я являюсь участником gapminder developers team, которая помогает компании gapminder foundation в разработке разнообразного стека модулей и ПО. gapminder foundation является некоммерческим предприятием, зарегистрированным в Стокгольме, Швеция, которое способствует устойчивому глобальному развитию и достижению Целей развития тысячелетия Организации Объединенных Наций за счет более широкого использования и понимания статистики и другой информации о социальном, экономическом и экологическом развитии на местном, национальном и глобальном уровнях. В следствии этого, мы, как разработчики, в основном занимаемся обработкой, хранением, визуализацией разнородных статистических данных а также обучаем этому всех желающих (по мере наших скромных сил) Одной из задач в нашем случае была обработка большого количества csv файлов (> 1Gb, или даже > 1Tb): т.е. это не просто перебрать строки csv файла и засунуть их в БД, а разобрать каждую на запчасти, посмотреть что эта строка означает в рамках того источника данных и определить ее место и назначение в БД. Кроме этой достаточно понятной функции нам необходимо было учитывать версионность этих файлов (в 1000000 строке обновились, добавились или удалились данные, и надо определить что это означает для всего источника, как это влияет на другие связанные сущности). Разрабатывать мы начали на nodejs, т.к. этот язык был известен и понятен для всей gapminder developers team. Также важно было, чтобы мы тратили как можно меньше дорогих ресурсов (таких как оперативная память, количество и мощности процессоров), пользуясь по максимуму самыми дешевыми (дисковое пространство, время обработки)

Задача:

в гит коммитах находятся большие csv файлы (> 1Gb, или даже > 1Tb), которые время от времени иногда значительно иногда незначительно меняются. Надо научиться находить diff между 2мя коммитами и только его складывать в БД.

Ограничения:

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

Размышления:

ну вроде как все понятно. в самом общем случае: есть 2 больших файла (для начала > 1Gb) в разных коммитах, хотим получить дифф между ними, чтобы потом его обработать (сложить в бд). Получаемый дифф должен содержать некоторое состояние на каждое найденное изменение - MAD (Modified, Added, Deleted).

Вопросы:

  1. должно ли для диффа должно иметь значение
  • если поменяются 2 строки местами
  • если поменяются 2 колонки местами
  • если переименются колонки
  1. какие изменения в файлах для нас не важны
  2. что будут означать состояния M | A | D для каждого изменения (т.е. как их потом обрабатывать)

Что использовать?

  • стримы

Ячейки в каждом файле это не просто ячейки, а связанные сущности, и эти связи также надо было обновлять, а значит надо было иметь какое-то промежуточное хранилище. Поэтому дифф который мы создавали - хранился в файле (максимально экономим на оперативной памяти). Благо в жестком диске ограничений у нас не было и мы решили воспользоваться им. У нас был еще один козырь в рукаве, которого у вас может не оказаться: у нас был специфичный формат csv файлов, на который мы могли опереться.

Описание идеи (параллельная потоковая обработка файлов)

уникальное значение особой колонки (или набора колонок) которое получаем из старой файла записывались в WeakSet. Если они совпадали

  1. Сначала проверяем структуру файлов. Это наиболее глобальные изменения, которые затрагивают все строки (и хотелось не плодить сущности без необходимости):
    • добавилась колонка(и)
    • удалилась колонка(и)
    • изменилась колонка(и) (update)
  2. Затем проверяем содержимое (без учета изменений в структуре файлов)
    • добавилась строка(и)
    • удалилась строка(и)
    • изменилась строка(и) (change)

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

Шутиха

как ржали над тем, что сами каждый раз забывали что значит update, а что change

Поиски:

Нашли модуль daff, который потоком выдавал все изменения которые были произведены в файлах (основываясь на команде git diff). Оставалось только отфильтровать неинтересные нам, добавить расширенную информацию для постобработки (была строчка такая, стала такая, вид изменения такой-то, в таком-то файле и т.д.) и сохранять дифф в файл (в заранее оговоренном формате). Подробно разберем как работает daff. А также как работает дифф гита.

Подробно разберем как работает git diff

Подробно разберем как работает daff

Проверка решения под нагрузкой

(сколько съедало озу, проца при повышении размера файлов и количества изменений в них)

Плюхи:

  • использование daff для просмотра диффа для csv'шек в git
  • использование плагина для просмотра диффа для csv'шек в github под хром

Какие проблемы возникали:

как все упало на обновлении гита с 2.7.4 версии до 2.11.3 и в чем оказалась ошибка (в чейнджлогах git ничего не было, нашли по коммиту изменения, которые оказались для нас критическими)

Спецификация ndjson

выплевывали результаты в файл в формате ndjson (ссылка на спеку), а также описание формата для постобработки.

Тестируемость

непросто тестировать: одно и то же изменение можно определить как удаление и добавление или как модификацию! И все зависит от версии гит! Например переименованный файл в некоторых версиях гита не считается переименованным, а удаленным и созданным заново.

Выводы:

  • зная как строится ПО можно и нужно применять уже опробованные подходы (протестированные многими пользователями)
  • всегда стоит искать уже готовую реализацию, но обязательно проверять как именно она работает
  • всегда стоит фиксировать версию тех тулзов, которыми пользуетесь (ведь семвер - это договоренность, и далеко не всегда разработчики софта ее соблюдают)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment