MySQL tips: optimize tables

3 июня 2009

Задача: оптимизировать все таблицы в базе.

Решение:

mysql -u user -ppassword database -e "show tables"\
   | grep -v Tables_in | grep -v "+" \
   | gawk '{print "OPTIMIZE TABLE  " $1 ";" }' \
   | mysql -u user -ppassword database

MySQL tips: change all tables engine for database

26 мая 2009

Задача: Конвертнуть одним махом все таблицы из MyISAM в InnoDB

Решение:

mysql -u user -ppassword database -e "show tables"\
  | grep -v Tables_in | grep -v "+" \
  | gawk '{print "ALTER TABLE " $1 " ENGINE = InnoDB;"}' \
  | mysql -u user -ppassword database

Factory girl: extract_factories

19 мая 2009

Все наверное слышали о фабиках от Thoughtbots, а кто не слышал услыште.

Ставим все очень просто:

 gem install thoughtbot-factory_girl --source http://gems.github.com

А дальше курим доку и радуемся жизни.

О птичках, наваял тут скриптец для построения фабрик, который достаточно скопировать себе в директорию lib/tasks/

Использовать:

rake db:extract_fixtures

Генерит туповато но иногда полезно, особенно когда большой список аттрибутов у модели.

shoulda: running concrete context

5 мая 2009

Многие Rails разработчики уже используют shoulda - замечтельный  BDD фреймворк от команды thoughtbot, как альтернативу rspec, например.

Сам фреймворк - это надстройка над UnitTests.

При разработке с  UnitTests часто возникает необходимость запустить отдельный тест и посмотреть результаты. Но как быть с shoulda - ведь названия тестов здесь это обычные текстовые предложения, которые формируются с учетом контекста теста и контекстов в котором тест находится. Например:

require File.dirname(__FILE__) + '/../test_helper'
require 'shoulda'

class ArticleTest < Test::Unit::TestCase
  fixtures :all
  context "On create" do
    should "create article if all params ok" do
      article = Article.new( :title => "shoulda rocks", :body => "Try it")
    end
    should "require title" do
      article = Article.new(:body => "That article wouldn't be published")
    end
    should "require body" do
      article = Article.new(:title => "Where is my body?")
    end
  end
end

В таком случае shoulda сгенерит следующие тесты:

test: On create should create article if all params ok
test: On create should require title
test: On create should require body

Пока у нас не много тестов все ок. Но если у нас большое количество контекстов, например, On Create, On  Update, On Destroy и т.д.? И мы хотим выполнить определенный контекст для того чтобы понять где именно проблема.

Для этого воспользуемся возможностью UnitTests запускать отдельные тесты с ключем -n

Test::Unit automatic runner.
Usage: test/unit/shop_test.rb [options] [-- untouched arguments]

    -r, --runner=RUNNER          Use the given RUNNER
                                 (c[onsole], f[ox], g[tk], g[tk]2, t[k])
    -n, --name=NAME              Runs tests matching NAME.
                                 (patterns may be used).
    -t, --testcase=TESTCASE      Runs tests in TestCases matching
                                 TESTCASE. (patterns may be used).
    -I, --load-path=DIR[:DIR...] Appends directory list to $LOAD_PATH.
    -v, --verbose=[LEVEL]        Set the output level (default
                                 is verbose). (s[ilent],
                                 p[rogress], n[ormal],v[erbose])
        --                       Stop processing options so that the
                                 remaining options will be passed to the
                                 test.
    -h, --help                   Display this help.
Deprecated options:
        --console                Console runner (use --runner).
        --gtk                    GTK runner (use --runner).
        --fox                    Fox runner (use --runner).

Как показывает нам справка, параметром ключа -n может быть любое регулярное выражение. Например, команда:

ruby test/unit/article_test.rb --name="/test:(.*)On create(.*)/"

запустит на выполнение только тесты из контекста On create.

Вот так вот.

rake gems:install

3 мая 2009

В версию rails 2 была добавлена интересная возможность установки зависимых gem библиотек используя только 1 команду. Для этого Вам необходимо вписать в environment.rb следующие строки, например для установки will_paginate:

    config.gem 'mislav-will_paginate', :version => '~> 2.3.8',
      :lib => 'will_paginate',
      :source => 'http://gems.github.com'

После этого в командной строке нужно набрать rake gems:install. Но не все так просто.

Если у Вас есть rake task в котором Вы, например включаете  environment.rb то Вы получите интересное сообщение, примерно такое:

sudo rake gems:install
(in /home/cthulhu/development/blog)
Missing these required gems:
  mislav-will_paginate  ~> 2.3.8

You're running:
  ruby 1.8.6.111 at /usr/bin/ruby1.8
  rubygems 1.3.2 at /home/cthulhu/.gem/ruby/1.8, /usr/lib/ruby/gems/1.8

Run `rake gems:install` to install the missing gems.

ИМХО это еще хуже чем драйвера для вашего CD на CD.

Существует пара хаков против такого поведения.

Плохой, ИМХО, - подхачить rail. Для этого в файле rails/lib/gems.rake в блоке:

namespace :gems do
  task :base do
    $gems_rake_task = true
    require 'rubygems'
    require 'rubygems/gem_runner'
    begin
      Rake::Task[:environment].invoke
    rescue
    end
  end
  ...
end

Нужно заменить:

 Rake::Task[:environment].invoke

на

begin
  Rake::Task[:environment].invoke
 rescue
end

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

Второй вариант, более преемлемый - дописать в  environment.rb небольшой хак вида:

if $rails_gem_installer
  class Rails::Initializer
    def load_application_initializers; end
  end
  config.gem 'mislav-will_paginate',
    :version => '~> 2.3.8', :lib => 'will_paginate',
    :source => 'http://gems.github.com'
end

Переменная $rails_gem_installer будет установлена когда Вы будете вызывать рейк таски из семейства установки гемов.

Так вот.

Может кто знает более правильные способы решения этой проблемы?

TDD mantra RED/GREEN/REFACTOR in tha Rails

30 апреля 2009

Все слышали о TDD мантре красный, зеленый, реорганизация. Чтобы все было именно так, в рельсах нужно 2 вещи autotest и плагин redgreen.

Создаем в корне прожекта файлик .autotest и пишем в него 1 строчку require ‘redgreen/autotest’. Далее стартуем как обычно:

autotest -rails

И вуаля, консоль с тестами красно/зеленая.

MySQL tips: drop all tables from database

29 апреля 2009

Маленький скриптец для удаления всех таблиц из БД:

mysql -u user -ppassword db_name -e "show tables"\
  | grep -v Tables_in | grep -v "+" \
  | gawk '{print "drop table " $1 ";"}' \
  | mysql -u user -ppassword db_name

Еще можно написать хранимую процедуру для этого, примерно так:

delimiter $$
DROP PROCEDURE IF exist drop_tables();
create procedure drop_tables()
begin
  SELECT
    @drop_sql:=concat('DROP TABLE IF EXISTS ', group_concat(table_name))
    drop_statement
  FROM information_schema.tables
  WHERE table_schema=database();
  IF (@drop_sql IS NOT NULL) THEN
    PREPARE stmt from @drop_sql;
    EXECUTE stmt;
    DROP PREPARE stmt;
  END IF;
end$$
delimiter ;

Вот так вот.

MySQL MyISAM + rails tests = bugs

29 апреля 2009

Пришлось мне на днях потюнить MySQL. Решил попробовать разные конфигурации на предмет максимальной производительности по моим задачам. Нарыл ряд статей по  улучшению производительности. Лучше всего ИМХО написано было сдесь.

Пред история. Сразу скажу что запросы у меня простые и короткие. Пара селектов по индесированым полям + инсерт + апдейт. Ничего сложного. Но! В один прекрасный момент СУБД просто брала и начинала жрать все ресурсы процессора как локально так и на продакшин сервере. Однодневный инвестигейт показал интересную вещь. После инсерта происходит перестрой индексов данной таблицы, что само по себе требует ресурсов процессорного времени и это время прямо пропорционально количеству записей в таблице. Таким образом, если мне нужно много циклов записей/селектов подряд то время каждого цикла начинает расти. И у меня оно дошло до 1го цикла в секунду. Это при том что мне нужно это сделать 300к-500к раз. Посчитав примерно во что мне это обойдется вышло что весь цикл будет длиться с недельку. Это естественно ни в какие рамки не лезет.

Посему начал рыть. Сначала в сторону тюнинга СУБД. В целом добился прироста производительности гдето на 10%. Но проблема как обычно была не в СУБД. Т.е. как-бы в СУБД но не в СУБД. В общем это не самое интересное.

Самое интересно это то, что вовремя тюнинга я настроил все базы на использования движка MyISAM. И тут начались спец-эффекты. Упало ряд тестов. Анализ кода ничего не показал. Анализ фикстур, откаты изменений (я работаю в команде посему начал грешить на последние комиты) все это ни к чему не привело.

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

Вердикт. Для тех кто использует MySQL в тестовых версиях используйте движек InnoDB, иначе могут выпасть очень даже интересные глюки. Или используйте PostgreSQL.

rubyjobs.ru

27 апреля 2009

Не перестает удивлять и радовать интересными сервисами команда Hash Train. Новое детище получило название rubyjobs.ru. Данный сервис позволяет в удобной и простой форме разместить объявления о поиске работы или вакансии, которые связанные с языком программирования Руби. Ресурсу всего несколько дней, а там уже насыпалось объявлений.

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

Движек я так понял ребята сделали сами и конечно же на рельсах.

Если кому интересно, за широкими спинами  Hash Train  еще и такие сервисы как RubyMag.ru и RubyFlow.ru. Все они рекомендованы к подписке через RSS.

acts_as_random_id

27 апреля 2009

Ребята из дружественного мне Hash Train написали отличный плагин для генерирования поля id для ваших рельсовых объектов. Как известно rails генерирует поле id простым автоинкриментом. Таким образом, существует возможность проверить сколько объектов всего в БД. Иногда это секретная информация.

Использую плагин acts_as_random_id  можно подсунуть любое значение поля id. Естественно на Вас ложиться вся ответственность за уникальность.

Детали на github.

BTW, если есть идеи по улучшениям и доработкам плагина, велком.

Блог работает на WordPress.
Подписка RSS: все записи, комментарии.