2017-01-27 11 views
0

Я думаю, что вопрос не указывает на реальную проблему, мне трудно сгладить ее точно и кратко.Как хорошо запоминать клиент подключения MySQL во внешнем модуле, используемом, например, Синатра?

У меня есть камень, который реализует т.е. MySQL-базы данных «запросы» (также вставки, обновления ...)

module DBGEM::Query 
    def self.client settings=DBGEM.settings 
    @@client ||= Mysql2::Client.new settings 
    end 

    def query_this 
    client.query(...) 
    end 

    def process_insert_that list_of_things 
    list_of_things.each do |thing| 
     # process 
     client.query(...) 
    end 
end 

Кроме того, этот драгоценный камень, используемый Синатра приложение сидит на запускающим веб-сервер, как пума ,

В Синатра-приложение теперь я могу

get '/path' do 
    happy = DBGEM::Query.query_this 
    # process happy 
    great = DBGEM::Query.process_insert_that 1..20 
    # go on 
end 

Мне нравится этот API и этот код должен открыть только одно соединение с базой данных.

Но, насколько я понял, потому что код в определении 'get' не является единственным, кто обращается к вещам DBGEM::Query в то время, могут произойти странные вещи (через условия гонки, общее внутреннее состояние?).

Есть ли умный способ сохранить хороший синтаксис и совместное использование без создания шаблонного объекта (query = DBGEM::Query.new() #...), обертывая материал в блок (DBGEM::Query.process do |query| #...)?

Приведенный выше пример, очевидно, упрощен. Обработка синатры может быть более сложной, запросы, фактически выполняемые в объекте службы и т. Д. Кроме того, afaiu в среде веб-сервера forking, GC уничтожит client (закрытие соединения - вот как реализуется mysql2).

ответ

1

Я думаю, что соединение не будет закрыто каждый раз.

@@client разделяется между DBGEM::Query объектом (в модулях Ruby и классами также являются объекты) и всеми экземплярами этого объекта (если быть точным: все экземпляры классов, к которым этот объект смешался).

Итак, эта переменная будет работать до тех пор, пока не будет проживать объект DBGEM::Query.

Вы можете проверить, когда объект DBGEM::Query будет собирать мусор, указав финализатор, запустив текст и наблюдая за консолью сервера.

module DBGEM::Query 
    ObjectSpace.define_finalizer(self, proc { print 'garbage collected' }) 
    .. 
    end 

Я не уверен, но я думаю, что DBGEM::Query объект будет мусора только тогда, когда вы остановите сервер.

Как это ни странно, «все может случиться», я считаю, что вы имеете в виду потенциальные конфликты, условия гонки, ситуации, когда вы создаете двойные записи, или обновляете одну и ту же запись, почти одновременно перезаписывая что-то и т. Д. И когда это вы теряете целостность данных.

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

+0

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

+2

Также «странные вещи» означали, что единственный объект-клиент mysql2 лишь частично подходит для использования в потоках и может показывать условия гонки (включая перекрывающиеся транзакции, неполные или неожиданные результаты ...). Хотя сам объект mysql2 нацелен на то, чтобы быть потокобезопасным, я был бы очень осторожен. –

+0

Когда он идет для финализатора, да, потому что переменная '@ @ client' живет до тех пор, пока объект модуля (а не объекты экземпляров), ссылка: The Well Grounded Rubyist, глава: 5. Объект по умолчанию (self), область видимости и видимость , – maicher

 Смежные вопросы

  • Нет связанных вопросов^_^