Статьи

Чат с Ником Куаранто О RubyGems.org Internals

RubyGems.org сделал это намного проще
для всех нас, чтобы внести вклад Ruby драгоценных камней

Ник Куаранто (@qrush) произвел революцию в разработке драгоценных камней в 2009 году, запустив новое хранилище драгоценных камней под названием Gemcutter.org. Внезапно впервые любой разработчик Ruby может опубликовать новый гем, просто запустив «gem push my_awesome_gem». Скорость и простота этого нового процесса привели к взрыву в разработке и публикации гемов Ruby. Позже Gemcutter.org был перемещен на RubyGems.org и стал стандартным хранилищем гемов сообщества Ruby.

Мне нравилось слушать чат Ника с RubyRogues о RubyGems.org пару недель назад, особенно рассказы о том, как Ник начал разрабатывать Gemcutter и его раннюю историю. Затем на прошлой неделе у меня была возможность пообщаться с Ником о том, как на самом деле работает RubyGems.org. Мне было любопытно узнать больше о том, что происходит на сервере, когда я загружаю новый гем-файл, как он так быстро обслуживает гемы и как он работает с новым API зависимостей Bundler 1.1. Вот несколько основных моментов нашего разговора …

Q: Привет, Ник, спасибо, что уделили мне время… Я очень ценю это!

Хейо — нет проблем.

Q: Я думал написать о том, как RubyGems.org работает внутри, и я решил … почему бы не спросить вас сначала? Итак, сегодня у меня есть несколько технических вопросов для вас, а также несколько диаграмм. Вы можете исправить меня и исправить ошибки в моих диаграммах 🙂

Конечно!

Нажимаем новый драгоценный камень на RubyGems.org

В: Что происходит на сервере RubyGems.org, когда кто-то запускает «gem push»?

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

  • Что ты нам дал?
  • Мы видели это раньше?
  • Вы тот, кому разрешено возиться с этим драгоценным камнем?
  • Тогда мы действительно сохраняем это.

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

толкает драгоценный камень

Gem Indexes

Q: Вы только что упомянули «индексы гемов» — что такое файлы индексов гемов и зачем они нужны RubyGems?

Индекс — это то, что gem fetch, gem install и список gem используют, чтобы выяснить, что доступно. Обновление этого индекса занимает некоторое время, потому что сейчас насчитывается почти 200 000 драгоценных камней.

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

В: Как вы создаете индексные файлы?

Я использовал для перестроения индекса сразу после нажатия, и это занимало около минуты, и пользователю просто нужно было подождать, пока он будет работать на сервере. Поэтому, когда я переместил его в Heroku, мне сказали, что это плохая идея, поскольку Heroku убивает запросы через 30 секунд. Я должен был найти лучший способ сделать это. Так вот как я узнал о delayed_job.

фоновое задание создает индексные файлы

Q: Сколько сейчас драгоценных камней? Сколько времени занимает фоновое задание delayed_job, чтобы перестроить индекс?

В настоящее время существует 177 000 проиндексированных драгоценных камней, и для создания всего индекса требуется около минуты или около того. Вот как долго вы должны ждать, пока ваш драгоценный камень будет установлен. И это в основном весь процесс.

Я думаю, что большинство людей просто предполагают, что, как только вы нажмете, это будет сделано, но на самом деле это не так. Я не думаю, что мы сейчас делаем хорошую работу, говоря им: «Эй, у вас есть хотя бы минутка ожидания». Я думаю, что люди привыкли к мгновенности этого сейчас.

индекс здания занимает 1 минуту

Обслуживающие драгоценные камни

Q: Как RubyGems.org обслуживает реальные файлы gem, когда разработчики запускают «gem install» или «bundle install»?

Приложение фактически начиналось как два маленьких приложения Sinatra. Один был для повторной реализации гем-сервера; это у всех на установке RubyGems, и именно так вы можете обслуживать драгоценные камни со своего компьютера. Я должен был переопределить это, потому что это должно было быть поддержано базой данных и потому что это должно было работать вне файловой системы. И другим приложением Sinatra был пользовательский интерфейс. Хотя в конце концов я дошел до того, что мне понадобился Rails, а не Sinatra, для пользовательского интерфейса.

Я не мог назвать приложение Sinatra «Gem Server», поэтому подумал: что ближе всего к серверу в ресторане… и сказал: «Хозяюшка!» «Хозяюшка» все еще там, и это все еще Синатра. И я думаю, что это хорошо для приложения Sinatra, потому что маршруты действительно странные, потому что вы сидите в файловой системе, обслуживающей реальные файлы, потому что именно так она и была построена изначально. И я думаю, что если бы они были маршрутами Rails, и они, безусловно, могли бы быть, они были бы странными и грубыми.

загрузка

Вот один из маршрутов из приложения Hostess Sinatra, о котором говорил Ник, который перенаправляет клиентов для загрузки файлов индекса драгоценных камней из S3:

class Hostess < Sinatra::Base
etc
def serve_via_s3
serve do
redirect «http://#{$rubygems_config[:s3_domain]}#{request.path_info}«
end
end
etc
%w[/specs.4.8.gz
/latest_specs.4.8.gz
/prerelease_specs.4.8.gz
].each do |index|
get index do
content_type(‘application/x-gzip’)
serve_via_s3
end
end
etc

view raw
hostess.rb
hosted with ❤ by GitHub

Q: Что такое CloudFront? Почему RubyGems.org использует это?

CloudFront используется для обслуживания драгоценных камней и gemspecs, потому что они никогда не меняются. CloudFront — это CDN, поэтому есть узлы по всему миру: некоторые в Японии, 1 или 2 в Европе, некоторые в США, Китае, и я думаю, что есть в Южной Америке. Они служат для нас большой загрузкой: драгоценные камни и гемспеки. Приятно то, что когда вы нажимаете те, что они атомарные и никогда не меняются, вам никогда не нужно обновлять их.

Redis и Bundler 1.1 API

Одна из наиболее интересных новых возможностей RubyGems.org — это быстрый API, который он предоставляет для получения информации о зависимостях гемов. Чтобы узнать больше об API и о том, как его использует Bundler 1.1, см. Мою статью за октябрь: почему Bundler 1.1 будет намного быстрее .

Q: Однажды я слышал, что вы использовали Redis, чтобы помочь реализовать API зависимостей, который использует Bundler 1.1. Почему вы использовали Redis для этого?

Мы хотели, чтобы это было действительно быстро, и я думаю, что когда Мэтт Монжо из Thoughtbot и я изначально играли с ним, мы, возможно, сначала пытались сделать это внутри Postgres, и запросы выглядели действительно грубыми и длинными. И мы знали, что данные не будут изменяться — я думаю, «данные не будут сильно меняться» — не очень хороший аргумент. Мне очень нравится Redis, и мне нравится возиться с ним, и мы довольно интенсивно его использовали для подсчета загрузок, я думаю, я просто хотел попробовать и посмотреть, насколько быстро это будет, и в итоге это было чертовски быстро, чтобы решить эту проблему. Первое дерево для зависимостей.

Если бы мы использовали Postgres для этого, это было бы большим объединением трех таблиц. И они тоже большие таблицы, особенно таблица зависимостей. Мы хотели, чтобы это было быстро — я не знаю, может быть, когда выйдет Bundler 1.1, все сгорит.

Использование Redis для зависимостей

Некоторые детали кода

В: Давайте подробнее рассмотрим класс Pusher, который вы упомянули ранее. [Pusher] — это код, который ловит новые драгоценные камни и переносит их в S3 и CloudFront. Я заметил, что он не основан на ActiveRecord и не соответствует таблице базы данных. Это хорошая идея в целом?

Я думаю, что все в порядке, чтобы иметь модели, которые ActiveRecord-иш. Тем не менее, когда я писал это, это было до появления ActiveModel, поэтому я хотел бы, чтобы он использовал валидации, и он вроде уже использует «сохранить».

Вот небольшая часть класса Pusher, который мы обсуждаем; это код, который обрабатывает гемы, которые помещаются на RubyGems.org:

class Pusher
attr_reader :user, :spec, :message, :code, :rubygem, :body, :version, :version_id
def initialize(user, body, host_with_port=nil)
@user = user
@body = StringIO.new(body.read)
@indexer = Indexer.new
@host_with_port = host_with_port
end
def process
pull_spec && find && authorize && save
end
etc

view raw
pusher.rb
hosted with ❤ by GitHub

В: Как насчет метода «процесс» — почему вы решили написать его таким образом, вызывая 4 других метода? Это образец, которому другие люди должны учиться и подражать?

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

Другие участники

В: Кто еще помог вам на RubyGems.org? Есть ли другие участники?

Первые два человека, которые нужно упомянуть, это Том Коупленд ( github twitter ) и Эван Феникс ( github twitter ). Они очень помогали! Эван входит в основную команду RubyGems, поэтому он много знает о том, что происходит на стороне клиента, о чем я понятия не имею. Том был нашим сисадмином некоторое время; он также был системным администратором RubyForge.

Эти два парня очень помогли. Кроме того, совсем недавно я выступил с призывом к коммиттерам, и мне помогали три парня:

Они очень помогали; Эрик написал оболочку командной строки для API под названием « драгоценные камни », и он в основном проник в мой мозг при реализации API, и ему нужно было обсудить со мной вещи, которые решают психические проблемы, которые у него были с моим мозгом 🙂 Поэтому я решил: если Вы хотите исправить все эти вещи, тогда вы можете помочь мне с этим! Он много помогал с запросами на получение информации и постоянно обновлял сайт.

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

И Крис, который работает на Swipely.com в Провиденсе, отлично справился не только с исправлением ошибок, но и хотел помочь улучшить нашу инфраструктуру. Я бы сказал, что те 5 помогли с тонны.

В чем еще вам нужна помощь?

В: Помимо зеркалирования и API графика загрузки, которые вы упомянули в подкасте RubyRogues, с чем еще вам нужна помощь?

Все в списке вопросов открыто, если вы хотите работать над этим, у нас есть много людей, которые объединят ваши материалы. Если вам нужны более «мягкие» вещи, которые не связаны с кодом, у нас есть справочный сайт ( help.rubygems.org ), на котором много вопросов, на которые часто можно ответить, не имея кого-либо с доступом к серверу. Есть также много вопросов о StackOverflow.

Другое дело, что у нас есть сайт «путеводителей» ( guides.rubygems.org ). Здесь есть учебные пособия — как мне делать определенные вещи? Если вы думаете, что есть вещи, с которыми люди сталкиваются, вы можете выбросить такие вещи здесь. Например, у меня есть небольшой список встреч, которые я нашел, учебники и еще много чего на странице ресурсов .

Не бойтесь ошибиться с кем-нибудь из авторов, особенно если вы ищете что-то для себя. Я более чем рад помочь людям внести свой вклад!

Благодарность!

Спасибо, что уделили мне время, Ник. Было очень приятно смотреть на твой код и пытаться понять, что происходит. Большое спасибо, Ник! До свидания…

Я рад, что ты не убежал, крича! Это хороший знак … спасибо, чувак.