среда, 27 июля 2016 г.

Git - минимизация merge-commit'ов в master

У нас небольшая команда разработки и, зачастую, мы правки делаем прямо в master.
В среднем у нас 16 commit'ов в день (от 1 до 45), поэтому периодически сталкиваюсь с тем, что кто-то за'push'ил в мастер в центральный репозиторий одновременно со мной, а я забыл с'pull'иться перед своим commit'ом. После слияния веток возникают грязные commit'ы вида:

845f25b 2016-07-26 (Some User) -  Merge branch 'master' of github.com:Company/repository

Чтобы избегать commit'а в мастер, когда в центральном репозитории кто-то что-то поменял, можно использовать hook на pre-commit:

cat .git/hooks/pre-commit

#!/bin/sh

red="\033[01;91m"
green="\033[01;32m"
reset="\033[0m"

branch=`git rev-parse --abbrev-ref HEAD`
if [ a$branch == amaster ]
then
git fetch origin master >/dev/null 2>&1
local_commit=`git rev-parse --verify HEAD`
origin_commit=`git rev-parse --verify origin/master`
#if [ a$local_commit != a$origin_commit ]
if ! git log | grep -qs $origin_commit
then
echo
echo "${red}origin/master ушел вперед.${reset} Сделай сначала: ${green}git pull origin master${reset}"
echo
exit 1
fi
fi
exit 0

Надо отметить, что такой подход не гарантирует отсутствие merge-commit'ов, но если все свои изменения сразу после commit'а push'ить в центральный репозиторий, то таких ситуаций будет крайне мало.

понедельник, 18 января 2016 г.

Как склонировать SD-карту под Mac OS X

Возникла задача, нужно сделать 6 клонов для одной SD карты.

Нашел в сети описание, как склонировать SD-карту под Mac OS X.

sudo dd if=/dev/disk2 of=/Users/ruzin/raspberrypi.dmg
sudo dd of=/dev/disk2 if=/Users/ruzin/raspberrypi.dmg

Процесс занял нереальное время (я даже не дождался создания первого дубликата - прервал через 45 минут). В моем случае скорость копирования с диска на карту составляла 1 Мбайт/с. Потом нашел способ, как поднять скорость в 13 раз (до ~13 Мбайт/с): 

Процедура:

  1. Вставляем исходную карту и делаем копию образа к себе на диск:

    sudo dd if=/dev/rdisk2 of=/Users/ruzin/raspberrypi.dmg bs=1m
  2. Перед заменой карты отмонтируем ее от файловой системы:

    diskutil unmountDisk /dev/disk2
  3. Вставляем пустышку (она автоматически монтируется) и отмонтируем ее:

    diskutil unmountDisk /dev/disk2
  4. Копируем на нее образ (и отмонтируем перед вытаскиванием):

    sudo dd of=/dev/rdisk2 if=/Users/ruzin/raspberrypi.dmg bs=1m
    diskutil unmountDisk /dev/disk2

Копия диска заняла ~10 минут для карты размером в 8GB.

среда, 23 декабря 2015 г.

При использовании конструкции:

LOAD DATA LOCAL INFILE '/path/to/file.data' REPLACE INTO TABLE `table` (id, ...)

Столкнулся с проблемой:

EException django.db.utils.OperationalError: OperationalError(1148, 'The used command is not allowed with this MySQL version') in <bound method MySQLLoader.__del__ of <libs.db.loader.MySQLLoader object at 0x10c0ccb90>> ignored

На моем Mac вылечилось добавлением в /etc/my.cnf двух строк:

[mysqld]
local_infile=1...
[client]
local_infile=1...

среда, 7 октября 2015 г.

Удаление значительного числа строк из большой таблицы MySQL

Если у вас большая таблица (скажем, 10+ млн.строк), то удаление значительной части строк (скажем, 20+%) из нее может стать проблемой.


  1. Во-первых, эта операция будет занимать длительное время и некоторые операции на выборку/модификацию данных в эту таблицу все это время нельзя будет произвести.
  2. Во-вторых, удаление строк не освободит место на диске.


Опишу алгоритм, который я несколько раз использовал, когда мне нужно было провести подобную операцию:
  1. создать клон таблицы:

    create table <new_table> like <old_table>
  2. запомнить максимальный id в старой таблице:

    select max(id) from <old_table>
    Записываем на лист бумаги ;-)
  3. переставить id (автоинкрементный) в новой таблице на это max(id) + запас (скажем +2 миллиона):

    ALTER TABLE <new_table> AUTO_INCREMENT=<old_value>+2000000

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

    insert into <new_table> select * from <old_table> where ...
  5. переименовать обе таблицы:

    RENAME TABLE <old_table> TO <very_old_table>, <new_table> TO <old_table>

    Да, можно делать переименование двух таблиц за одну операцию.
  6. проверить, что максимальные ID в обеих таблицах совпадают. Если не совпадают, докопировать недостающие записи из старой таблицы в новую:

    insert into <new_table> select * from <old_table> where ... and id > <max(id)>

    <max(id)> берем из второго шага.
  7. удалить старую таблицу

    drop table <very_old_table>

Обещал пару трюков к шагу 4:

Что делать, если условие по определению хороших или плохих строк очень сложное? Можно отдельными запросами сохранить хорошие или плохие ID в отдельную таблицу (с одним полем ID). А потом при-JOIN-ить эту таблицу к нашему селекту.

Для хороших:

insert into <new_table> select * from <old_table> join <good_ids> on <old_table>.id=<good_ids>.id

Для плохих:

insert into <new_table> select * from <old_table> left join <good_table> on <old_table>.id=<bad_ids>.id where <bad_ids>.id is NULL

Чтобы JOIN'илось быстро, следует по колонке id в таблице good/bad_ids построить индекс.

воскресенье, 28 июня 2015 г.

Во что обойдется скачать весь интернет?

Читая новость о том, что http://archive.org/web/ претерпевает изменения, задумался во что обойдется скачать весь интернет?

Для начала решил оценить объем информации:
Google проиндексировал 46 млрд. страниц (http://www.worldwidewebsize.com/).
В действительности, страниц может быть больше, т.к. не все заслуживают индексации.

60Kb - средний размер HTML-страницы (https://gigaom.com/2014/12/29/the-overweight-web-average-web-page-size-is-up-15-in-2014/)
Верить этому числу нельзя, но за неимение лучшего...

Итого требуется скачать ~3000 ТБ (это не считая картинок, видео, музыки и пр.)

Далее нам нужны сервера, каналы и хранилище.
Для скачивания информации подойдут самые простые сервера, т.к. нагрузка на CPU - минимальная.
Подключение к интернету желательно быстрое - из мейнстрима - 1Gbit/sec (пиковая 10Тб/день, на практике, раза в 3 меньше)
Хранилище - самые большие из самых дешевых дисков (4Тб/диск)

Если набирать конфигурацию в дата-центре serverloft, предпочитаемом мной с точки зрения качество/цена.

$214 за конфигурацию с 16TB SATA на канале в 1Gbit/sec

Итого нам потребуется ~190 серверов и 5 дней (если дата-центр сможет обеспечить суммарный объем входящего трафика 63Gbit/sec).

Обойдется все это удовольствие в ~$40000/месяц

В своих расчетах я не учитывал:

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

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

Я оптимист? ;-)

суббота, 28 марта 2015 г.

Пара фишек для разработчиков JavaScript & MongoDB

После просмотра туториалов EventedMind по-мимо основного содержимого вынес две полезные штуки:

  1. JavaScript: ключевое слово debugger приостанавливает выполнение кода (в Chrome). После чего можно будет по шагам пронаблюдать, что происходит.

    var a = 3;
    debugger;
    // some code we want to debug
    _
  2. MongoDB: с консоли mongodb можно посмотреть результат выборки, обычно он выводится в неудобоваримом виде:

    > db.collection.find()

    {"_id" : "WHdEadDwWnwn7GYzg", "createdAt" : ISODate("2014-12-09T11:24:32.154Z"), "emails" : [{"address" : "dasha@yandex.ru"}], "profile" : {"name" : "Даша"},...}

    Это легко исправить, достаточно приписать .pretty()

    > db.collection.find().pretty()

    {
      "_id" : "WHdEadDwWnwn7GYzg",
      "createdAt" : ISODate("2014-12-09T11:24:32.154Z"),
      "emails" : [
        {
          "address" : "dasha@yandex.ru"
        }
      ],
      "profile" : {"name" : "Даша"},
      ...
    }





воскресенье, 18 января 2015 г.

Мозг тренирует себя на отвлечения

В книжке "Sort you brain out" Dr.Jack Lewis & Adrian Webster нашел интересную главу:
Brain training ourselves to distraction.

Краткая цитата на английском:
... people who regularly use technology to do multiple tasks at the same time are less able to ignore distractions than those who don't.
  и мой вольный перевод:
... люди, которые используют инструменты для одновременного решения задач, хуже сопротивляются отвлечениям, чем остальные.
Инструментов, помогающие вам быть в курсе текущих событий, препятствует вашему движению. Сохраняйте баланс. Реже проверяйте почту/skype и т.п. - больше делайте :)

понедельник, 8 декабря 2014 г.

Переделываем под себя чужой пакет для Meteor.

Есть замечательный пакет reactive-table, который позволяет сэкономить массу времени на выводе коллекции объектов в табличном виде. В этом пакете из коробки работают:

  • сортировка
  • настройка отображения только части полей
  • кастомизация полей
  • фильтрация по подстроке

Собственно, с фильтрацией и была основная проблема.

Для отрисовки таблицы нужно вызвать шаблон:


{{> reactiveTable collection=cases settings=tableSettings}}

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

По дизайну моего приложения строка фильтра должна была находиться среди других управляющих элементов и совсем не справа. Разработчик reactive-table не предлагает возможности разместить этот элемент в другом месте. Загонять элемент на нужное место ухищрениями CSS мне показалось неправильным.

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

{{> reactiveTableFilter settings=tableSettings space='cases'}}
...
<!-- немного HTML -->
...
{{> reactiveTable collection=cases settings=tableSettings space='cases'}}

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

По шагам:
  1. Форкнуть проект на github: https://github.com/ecohealthalliance/reactive-table.git
  2. cd my-project
  3. mkdir packages
  4. cd packages
  5. git clone https://github.com/a-ruzin/reactive-table.git
  6. добавим ссылку на оригинальный репозиторий, чтобы можно было pull'ить изменения
    git remote add up https://github.com/ecohealthalliance/reactive-table.git
  7. cd reactive-table
  8. 4-ую строку package.js заменим на: "xxx:reactive-table"
  9. cd .. 
  10. meteor add xxx:reactive-table

В четвертом пункте вместо 'xxx' может быть любая строка (например, ваш аккаунт на meteor.com).

Все, после этого уже можно править код.

Желательно сразу переключиться на другую ветку, чтобы уменьшить количество проблем с merge, если ваши изменения окажутся кривыми или за время разработки основной разработчик убежит вперед:

  1. git checkout -b my-feature
Теперь мы свободно меняем исходник пакета и проверяем его работу на своем живом проекте.

После того, как все отладили, commit'им и затаскиваем в свой репозиторий на github.com.
  1. git commit -am "my feature"
  2. git push origin my-feature
Если вы сочли ваши изменения полезными для других можно сделать pull-request автору оригинального пакета.


пятница, 13 июня 2014 г.

MeteorJS

Начал изучение MeteorJS. https://www.meteor.com/

Первое впечатление - лучшее средство для прототипирования web-приложений.

Из существенных плюсов:

  1. Разработка полезного кода через минуту после старта проекта (вам не надо готовится, придумывать структуру проекта, настраивать окружение, поднимать веб-сервер и пр.)
  2. "Моно"-культура (если считать JavaScript+HTML единой платформой, то кроме этого ничего больше не понадобится - ни Python, Ruby, PHP, C# и т.п.)
  3. Запуск "серверного" JavaScript. Часть кода обязана запускаться на сервере, чтобы гарантировать отсутствие взлома.
  4. Reactive programming. Вы можете представления своих объектов сделать "живыми", т.е. если кто-то другой их меняет, то эта информация тут же доходит до вашего приложения и представления автоматически показывают обновления. Работает "из коробки".
  5. Удобная система шаблонов. 
  6. Автоматическая сборка JS/HTML - можно раскидать файлы проекта почти "как угодно", проект соберется и будет работать - т.е. вы можете разместить файлы на свой вкус, и при этом не заморачиваться с импортами из нужных каталогов.
  7. LiveCodeUpdate - как только вы внесли изменение в код, он автоматически обновляется на клиентах (особенно удобно для отладки приложений) - вам не надо перезагружать браузер. При использовании рекомендаций (типа хранение параметров в Session) пользователь может даже не заметить, что код поменялся.
  8. Packages. Система пакетов - с этим еще предстоит разобраться, но есть масса готовых пакетов (я использую: iron-router, collection2, simple-schema, autoform) и возможность писать и публиковать свои: https://atmospherejs.com/.
Написано пока мало кода, есть свои сложности с освоением, но, в целом, очень приятная среда.

четверг, 6 марта 2014 г.

MySQL: VARCHAR vs TEXT

После обновления командой разработки структуры БД появилась таблица со 114 колонками, из которых 15 типа LOGTTEXT. По факту MySQL начал тормозить, даже на операциях, которые не должны вызывать проблем (обновление одного поля по первичному ключу). 
Мои изыскания на тему, может ли это быть основной/единственной причиной тормозов не дали положительного результата. Вот ссылки крупицы знаний, которые удалось найти:


TEXT and BLOB is stored off the table with the table just having a pointer to the location of the actual storage.
VARCHAR is stored inline with the table. VARCHAR is faster when the size is reasonable, the tradeoff of which would be faster depends upon your data and your hardware, you'd want to benchmark a realworld scenario with your data.
http://stackoverflow.com/questions/2023481/mysql-large-varchar-vs-text  


MySQL needs to allocate TEXT column separately which causes some overhead, but I guess it is minimal.
http://forums.mysql.com/read.php?24,105964,105984#msg-105984

TEXT data types are stored as separate objects from the tables and result sets that contain them. This storage is transparent — there is no difference in how a query involving a TEXT field is written versus one involving a VARCHAR field. Since TEXT is not stored as part of a row, retrieval of TEXT fields requires extra [edited 1/22] memory overhead.
http://www.pythian.com/blog/text-vs-varchar/
Если подытожить, то дополнительный расход есть, но он не должен быть значительным.

Тем не менее, мы от`ALTER`или таблицу и заменили LONGTEXT'ы на VARCHAR (благо в нашем случае объемные тексты не применялись).

О результатах сообщу дополнительно...

... похоже помогло.

После переделки LONGTEXT -> VARCHAR скорость вернулась на прежний уровень.