2016-07-29 4 views
12

मुझे लंबे समय से उपभोग करने वाले माइग्रेशन के साथ समस्या है, जिसे मैं समानांतर में चलाने के लिए वांछित था (इसे समानांतर में चलाया जा सकता है)। दरअसल माइग्रेशन डेटाबेस में सभी रिकॉर्ड लेने और उनमें से प्रत्येक पर संसाधन-उपभोग करने के संचालन को लागू करने के बारे में है।पूलबॉय में टाइमआउट कैसे संभालें?

कभी-कभी व्यक्तिगत रिकॉर्ड माइग्रेशन लटक रहा है, इसलिए मैं समाप्त करने के लिए 10 मिनट देता हूं। अगर माइग्रेशन समाप्त नहीं हुआ है, तो मैं इसे बिना किसी अपवाद के नीचे बंद कर देना चाहता हूं (नीचे देखें)

मैं प्रवासन को समानांतर करने के लिए poolboy एरलांग पैकेज का भी उपयोग कर रहा हूं क्योंकि माइग्रेशन न केवल समय का उपभोग करता है, बल्कि संसाधन भी। समस्या यह है कि मुझे नहीं पता कि टाइमआउट होने पर त्रुटि को कैसे संभालना है और कोड तोड़ने जा रहा है। मेरे पर्यवेक्षण पेड़ है:

defmodule MyReelty.Repo.Migrations.MoveVideosFromVimeoToB2 do 
    use Ecto.Migration 

    alias MyReelty.Repo 
    alias MyReelty.Repo.Migrations.MoveVideosFromVimeoToB2.Migrator 

    # parallel nature of migration force us to disable transaction 
    @disable_ddl_transaction true 

    @migrator_waiting_time 10 * 60 * 1000 # timeout 
    @poolboy_waiting_time @migrator_waiting_time + 10 * 1000 # give a time for graceful shutdown 

    @pool_name :migrator 
    @pool_size 3 
    @pool_config [ 
    { :name, { :local, @pool_name }}, 
    { :worker_module, Migrator }, 
    { :size, @pool_size }, 
    { :max_overflow, 0 }, 
    { :strategy, :fifo } 
    ] 

    def up do 
    children = [ 
     :poolboy.child_spec(@pool_name, @pool_config) 
    ] 
    opts = [strategy: :one_for_one, name: MyReelty.Supervisor] 
    Supervisor.start_link(children, opts) 

    rows = Review |> Repo.all 

    IO.puts "Total amount of reviews is: #{length(rows)}" 

    parallel_migrations(rows) 
    end 

    def parallel_migrations(rows) do 
    Enum.map(rows, fn(row) -> 
     pooled_migration(@pool_name, row) 
    end) 
    end 

    def pooled_migration(pool, x) do 
    :poolboy.transaction(
     pool, 
     (fn(pid) -> Migrator.move(pid, { x, @migrator_waiting_time }) end), 
     @poolboy_waiting_time 
    ) 
    end 

    defmodule Migrator do 
    alias MyReelty.Repo 
    alias MyReelty.Review 

    use GenServer 

    def start_link(_) do 
     GenServer.start_link(__MODULE__, nil, []) 
    end 

    def move(server, { params, waiting_time }) do 
     GenServer.call(server, { :move, params }, waiting_time) 
    end 

    def handle_call({ :move, result }, _from, state) do 
     big_time_and_resource_consuming_task_here  
     {:reply, %{}, state} 
    end 
    end 
end 

समस्या है, तो डेटाबेस में कुछ रिकॉर्ड के प्रवास से अधिक 10 मिनट मैं अपवाद के इस प्रकार है लेता है:

20:18:16.917 [error] Task #PID<0.282.0> started from #PID<0.70.0> terminating 
** (stop) exited in: GenServer.call(#PID<0.278.0>, {:move, [2, "/videos/164064419", "w 35th st Springfield United States Illinois 60020"]}, 60000) 
    ** (EXIT) time out 
    (elixir) lib/gen_server.ex:604: GenServer.call/3 
    (poolboy) src/poolboy.erl:76: :poolboy.transaction/3 
    (elixir) lib/task/supervised.ex:94: Task.Supervised.do_apply/2 
    (elixir) lib/task/supervised.ex:45: Task.Supervised.reply/5 
    (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3 
Function: #Function<5.53617785/0 in MyReelty.Repo.Migrations.MoveVideosFromVimeoToB2.parallel_migrations/1> 
    Args: [] 

20:18:16.918 [error] GenServer MyReelty.Repo terminating 
** (stop) exited in: GenServer.call(#PID<0.278.0>, {:move, [2, "/videos/164064419", "w 35th st Springfield United States Illinois 60020"]}, 60000) 
    ** (EXIT) time out 
Last message: {:EXIT, #PID<0.70.0>, {:timeout, {GenServer, :call, [#PID<0.278.0>, {:move, [2, "/videos/164064419", "w 35th st Springfield United States Illinois 60020"]}, 60000]}}} 
State: {:state, {:local, MyReelty.Repo}, :one_for_one, [{:child, #PID<0.231.0>, DBConnection.Poolboy, {:poolboy, :start_link, [[name: {:local, MyReelty.Repo.Pool}, strategy: :fifo, size: 1, max_overflow: 0, worker_module: DBConnection.Poolboy.Worker], {Postgrex.Protocol, [types: true, username: "adik", types: true, name: MyReelty.Repo.Pool, otp_app: :my_reelty, repo: MyReelty.Repo, adapter: Ecto.Adapters.Postgres, database: "my_reelty_dev", hostname: "localhost", extensions: [{Geo.PostGIS.Extension, [library: Geo]}, {Ecto.Adapters.Postgres.DateTime, []}, {Postgrex.Extensions.JSON, [library: Poison]}], pool_size: 1, pool_timeout: 5000, timeout: 15000, adapter: Ecto.Adapters.Postgres, database: "my_dev", hostname: "localhost", pool_size: 10, pool: DBConnection.Poolboy, port: 5432]}]}, :permanent, 5000, :worker, [:poolboy]}], :undefined, 3, 5, [], 0, Ecto.Repo.Supervisor, {MyReelty.Repo, :my_reelty, Ecto.Adapters.Postgres, [otp_app: :my_reelty, repo: MyReelty.Repo, adapter: Ecto.Adapters.Postgres, database: "my_reelty_dev", hostname: "localhost", extensions: [{Geo.PostGIS.Extension, [library: Geo]}], pool_size: 1]}} 

मैं Migrator को terminate/2 या handle_info/2 डालने की कोशिश की और इसके साथ खेलते हैं, लेकिन मैं इन कार्यों को भी शामिल नहीं किया गया है। मैं टाइमआउट कैसे संभाल सकता हूं और उन्हें अपने माइग्रेशन को तोड़ने से रोक सकता हूं?

मैं @ johlo के संकेत इस्तेमाल किया अद्यतन, लेकिन मैं अभी भी समय हो रही है बाहर। मेरे समारोह है:

def init(_) do 
Process.flag(:trap_exit, true) 
{:ok, %{}} 
end 

उत्तर

6

Migrator.move/2 (अर्थात GenServer.call) समारोह बार इसे बाहर पूरे MoveVideosFromVimeoToB2 प्रक्रिया दुर्घटना क्योंकि वास्तव में वही प्रक्रिया GenServer कॉल बनाता है जाएगा।

यहाँ समाधान pooled_migration में गुमनाम समारोह में टाइमआउट को पकड़ने के लिए है, की तरह कुछ (मैं अमृत वाक्य रचना साथ बहुत परिचित नहीं हूँ, इसलिए यह संकलन नहीं हो सकता है, लेकिन आप विचार प्राप्त होना चाहिए):

def pooled_migration(pool, x) do 
:poolboy.transaction(
    pool, 
    (fn(pid) -> 
     try do 
      Migrator.move(pid, { x, @migrator_waiting_time }) 
     catch 
      :exit, reason -> 
      # Ignore error, log it or something else 
      :ok 
     end 
    end), 
    @poolboy_waiting_time 
) 
end 

यह Migrator प्रक्रिया नहीं है, यह GenServerMigrator पर कॉल करता है जो हमें करता है और हमें try-catch की आवश्यकता होती है।

यह भी ध्यान रखें कि Migrator प्रक्रिया अभी भी नहीं चल रही है, यह अभी भी चल रहा है, GenServer call documentation में timeouts अनुभाग देखें।

अद्यतन: @asiniy टिप्पणी @poolboy_waiting_time जब एक नि: शुल्क Migrator वर्कर प्रोसेस के लिए इंतज़ार कर :infinity करने के लिए सेट किया जाना चाहिए ताकि poolboy.transaction समारोह एक टाइमआउट त्रुटि फेंक नहीं है में उल्लेख के रूप में। चूंकि अंततः बाहर निकल जाएगा, यह सुरक्षित है।

+0

यह काम करता है! वैसे, मैं यहां 'माइग्रेटर' प्रक्रिया को कैसे मार सकता हूं? – asiniy

+0

'निकास' को पकड़ते समय आप 'Process.exit/2] (http://elixir-lang.org/docs/stable/elixir/Process.html#exit/2) का उपयोग' 'kill 'भेजने के लिए कर सकते हैं 'माइग्रेटर' को संकेत दें। या आप इसे एक सामान्य 'जेनसेवर' संदेश भेज सकते हैं जो इसे रोकने के लिए कह रहा है, लेकिन यह केवल 'चाल' कार्य के साथ किया जाने पर संसाधित हो जाएगा। – johlo

+0

मुझे परीक्षण करने के लिए दो दिन दें और एक बक्षीस सौंपें, ठीक है? – asiniy

संबंधित मुद्दे