Ruby Threads и MySQL соединение

0

Я пытаюсь Ruby Threads, у меня есть простой скрипт, который должен перебирать json для получения определенных данных, все работает нормально, но в один момент оболочка показывает:

'_query': This connection is in use by: #<Thread:[email protected]:62 sleep> (Mysql2::Error)

Как я могу закрыть это соединение, которое мне нужно для данных.

И самое главное, потоки на самом деле правильные пути?

Это сценарий, он будет работать с crontab:

require 'firebase'
require 'conekta'
require 'json'
require 'savon'
require "crack"
require 'active_support/core_ext/hash'  #from_xml 
require 'nokogiri'
require 'xmlsimple'
require 'mysql2'

class Cron  

    def generate_activation_code(size = 10)
        charset = %w{ 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z}
        (0...size).map{ charset.to_a[rand(charset.size)] }.join
    end

    def construct()
        base_uri = 'FIREBASE_URL'
        file = File.open("FIREBASE_CREDENTIALS", "rb")
        firebase = Firebase::Client.new(base_uri, file.read)
        Conekta.locale = :es
        Conekta.api_key = 'MY_KEY'
        @response = firebase.get('users', nil)
        @client = Savon.client(wsdl: 'MY_URL', ntlm: ["user", "pass"] , :convert_request_keys_to => :camelcase )
        @client_mysql = Mysql2::Client.new(:host => "localhost", :username => "root", :password => "", :database => "masaldo_api")
    end

    def get_comision()
        last_validity = @client_mysql.query("SELECT comision * 100 as comision FROM configuration")
        last_validity.each do |validityr|
            @comision = validityr["comision"]
        end   
    end

    def create_transaction(sku, token, phone, userid, card)
        validity = @client_mysql.query("SELECT precio * 100 as precio_total, vigencia, descripcion, precio as precio_base FROM bluesoft_services_validity WHERE sku='#{sku}'")
        validity.each do |row|
            @vigencia = row["vigencia"]
            @descipcion = row["descripcion"]
            @precio = row["precio_total"]
            @precio_base = row["precio_base"].to_i
        end
        if @vigencia.to_i > 0
            last_current = @client_mysql.query("SELECT * FROM transactions WHERE number='#{phone}' ORDER BY trandate DESC LIMIT 1")                             
            last_current.each do |last|
                @trandate = last["trandate"]
                @trandate_result = @trandate.strftime("%Y%m%d %H:%M:%S")                        
            end              
        end
        @last_with_validty = (@trandate + (@vigencia).to_i.day).strftime("%Y-%m-%d") 
        @today = (Time.now).strftime("%Y-%m-%d") 
        if @last_with_validty == @today
           conekta_charges = Conekta::Order.create({
                            :currency => "MXN",
                            :customer_info => {
                                :customer_id => user['customer_id']
                        },
                        :line_items => [{
                            :name => @descipcion,
                            :unit_price => @precio.to_i,
                            :quantity => 1
                        },
                        {
                            :name => 'Comision de Recarga',
                            :unit_price => @comision.to_i,
                            :quantity => 1
                        }],
                            :charges => [{
                                :payment_method => {
                                    :type => "card",
                                    :payment_source_id => user['fav_card']
                            }
                        }]
                    })
                    if conekta_charges['payment_status'] == 'paid'
                        begin  
                            response = @client.call(:venta, message: { 'sku' => 'TELCPA100MXN', 'fechaLocal' => '20180117 14:55:00', 'referencia' => '818181818181', 'monto' => '100', 'id_cadena' => '30', 'id_tienda' => '30', 'id_terminal' => '1', 'folio' => 'LUCOPCIHOW' })                        
                            parameters = response.body
                            parameters.each do |response, data|
                                if data[:return][:respuesta][:codigo_respuesta] == 0
                                    puts data[:return][:respuesta]
                                else
                                    puts data[:return][:respuesta]
                                end
                            end 
                        rescue Exception => e  
                            puts e.message  
                            puts e.backtrace.inspect  
                        end 
                    end
        end  
    end

    def init()
        threads = []        
        hash = @response.body
        hash.each do |token , user|
            threads <<  Thread.new do 
                #Check if user is current for transaction if not need to check agenda
                if user['is_current']                   
                    self.create_transaction(user['sku'], token, user['phoneNumber'], user['customer_id'], user['fav_card'])   
                    user['addressBook'].each do |userid , user_address_book|
                        if user['is_current']   
                            replacements = { '+521' => '' }
                            phone_number =  user_address_book['phoneNumber'].gsub(Regexp.union(replacements.keys), replacements)                                                    
                            self.create_transaction(user_address_book['sku'], token, phone_number, user['customer_id'], user_address_book['fav_card'])
                        end
                    end
                end
            end            
        end
        threads.each { |t| t.join  }  
    end
end

classCron = Cron.new()
classCron.construct()
classCron.get_comision()
classCron.init()

С уважением

  • 0
    Стоит отметить, что если вы только начинаете работать с Ruby, то «новая» запись хеша часто менее беспорядочная: Mysql2::Client.new(host: 'localhost', username: ...) где { x: y } эквивалентно { :x => y } .
  • 0
    Я сделал это, спасибо. Любой совет по поводу открытого подключения MySQL, и темы верны?
Показать ещё 1 комментарий
Теги:
multithreading

1 ответ

0
Лучший ответ

Когда вы выполняете многопоточный код в Ruby, вам нужно быть осторожным, чтобы не использовать ресурсы, такие как соединения с базой данных между потоками, если только драйвер не делает это достаточно ясно, чтобы поддерживать такую работу. Те, с которыми я знаком, - нет, а Mysql2 не является потокобезопасным.

Вы можете использовать Thread[:db] для хранения локального подключения к базе данных в потоке, например:

def db
  Thread[:db] ||= Mysql2::Client.new(...)
end

Где вы можете обратиться к нему следующим образом:

db.query(...)

Это автоматически установит соединение по мере необходимости.

Стоит отметить, что mysql2 является низкоуровневым драйвером и не очень приятен в использовании. Абстракция более высокого уровня, такая как Sequel, обеспечивает ряд существенных преимуществ: миграция, (необязательный) модельный слой и очень надежный построитель запросов с поддержкой значений заполнителей и простое экранирование.

  • 0
    спасибо, когда все потоки закончены, я полагаю, что мне нужно закрыть соединение?
  • 0
    Если вы не создаете и не удаляете много потоков, это обычно не является проблемой, но если вы это делаете, вы должны приложить усилия, чтобы закрыть соединение при завершении потока.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню