【问题标题】:process_not_owner_of_odbc_connection :ODBC connectionprocess_not_owner_of_odbc_connection :ODBC 连接
【发布时间】:2018-08-13 12:22:49
【问题描述】:

有没有人在用erlang odbc查询的时候遇到过这个错误:

{:error, :process_not_owner_of_odbc_connection}

我正在编写一个连接池,并且 :ODBC.connect('conn string) 在 genserver 内运行,并且返回的 pid 进入状态...但是当我从 gen 服务器中获取该 PID 时,尝试使用该 pid 运行 :odbc.param_query 时出现上述错误...如果我们可以使用在 genserver 中创建的 pid,我们应该如何创建连接池?

有人有什么想法吗?

GenServer:

defmodule ItemonboardingApi.ConnectionPoolWorker do
    use GenServer

    def start_link([]) do
      IO.puts "Starting Connection Pool"
      GenServer.start_link(__MODULE__, nil, [])
    end

    def init(_) do
        :odbc.start
      {ok, pid} = :odbc.connect('DSN=;UID=;PWD=', [])
      IO.inspect pid
      {:ok, pid}
    end

    def handle_call(:get_connection, _from, state) do
      IO.puts "Inspecting State:"
      IO.inspect state
      IO.puts("process #{inspect(self())} getting odbc connection")
      {:reply, state, state}
    end
  end

调用函数:

def get_standards_like(standard) do
    :poolboy.transaction(
      :connection_pool,
      fn pid -> 
       connection = GenServer.call(pid, :get_connection) 
       IO.inspect connection

      val =
        String.upcase(standard)
        |> to_char_list

      # Call Stored Proc Statement
      spstmt = '{call invlibr.usp_vm_getStandards (?)}'

      case :odbc.param_query(connection, spstmt, [
             {{:sql_char, 50}, [val]}
           ])
           |> map_to_type(ItemonboardingCore.Standard) 
           do
        {:ok, mapped} ->
          {:ok, mapped}

        {:updated, affected_count} ->
          {:ok, affected_count}

        {:executed, o, a} ->
          {:ok, o, a}

        {:error, _} ->
          {:bad}
      end 
     end
    )
  end

我已确认 genserver 中的 pid 感染了正确的 odbc pid,并且 GenServer.Call 返回其中一个。

****编辑**** 这是我为解决此问题所做的工作。我不知道创建连接的进程必须是运行查询的进程。对我的工作人员进行细微更改以将查询传递给已解决了我的问题。这是一个艰难的第一步,还有一些事情需要对工人做。

defmodule ItemonboardingApi.ConnectionPoolWorker do
    use GenServer

    def start_link([]) do
      IO.puts "Starting Connection Pool"
      GenServer.start_link(__MODULE__, nil, [])
    end

    def init(_) do
      :odbc.start(:app)
      {ok, pid} = :odbc.connect('DSN=;UID=;PWD=', [])
      IO.inspect pid
      {:ok, pid}
    end

    def handle_call(:get_connection, _from, state) do
      {:reply, state, state}
    end

    def handle_call({:query, %{statement: statement, params: params}}, _from, state) do
      #TODO Check if pid is alive and start if needed.
      {:reply, :odbc.param_query(state, to_charlist(statement), params),  state}
    end
  end

【问题讨论】:

  • 如果连接更改为新连接,您还需要更新状态,如果无法建立连接,则需要更新状态。这与我所做的类似。
  • @RomanRabinovich 你有例子吗?
  • 在下面查看我的答案

标签: erlang odbc elixir


【解决方案1】:

这将是一个问题,就像 Kevin 指出的那样,您不能转移 odbc 和其他驱动程序(如 mysql/otp)的连接所有权。

如果你想使用连接池,可以看看这个https://github.com/mysql-otp/mysql-otp-poolboy

否则,您可以使用任何池,但执行 sql 查询的进程必须是打开连接的进程。


Erlang 中的示例

sql_query_priv(Conn, Conn_Params, {SQL, Params}) ->
    lager:debug("~p:sql_query_priv trying to execute query: ~p, with params: ~p, conn = ~p~n", [?MODULE, SQL, Params, Conn]),
    case Conn of 
    null -> try mysql:start_link(Conn_Params) of
            {ok, NewConn} ->  
                  lager:info("~p:sql_query_priv Connection to DB Restored", [?MODULE]),
                  try mysql:query(NewConn, SQL, Params) of
                    ok ->  {ok, NewConn, []};
                    {ok, _Columns, Results} -> {ok, NewConn, Results};
                    {error, Reason} ->  
                        lager:error("~p:sql_query_priv Connection To DB Failed, Reason: ~p~n", [?MODULE, {error, Reason}]),
                       exit(NewConn, normal),
                       {error, null, Reason}                            
                  catch 
                    Exception:Reason -> 
                        lager:error("~p:sql_query_priv Connection To DB Failed, Exception:~p Reason: ~p~n", [?MODULE, Exception, Reason]),
                        {error, null, {Exception, Reason}} 
                  end;
            {error, Reason} ->  
                   lager:error("~p:sql_query_priv Connection To DB Failed, Reason: ~p~n", [?MODULE, {error, Reason}]),
                   {error, Conn, Reason}                            
            catch 
                Exception:Reason -> 
                    lager:error("~p:sql_query_priv Connection To DB Failed, Exception:~p Reason: ~p~n", [?MODULE, Exception, Reason]),
                    {error, Conn, {Exception, Reason}} 
            end;
    Conn -> try mysql:query(Conn, SQL, Params) of
                ok ->  {ok, Conn, []};
                {ok, _Columns, Results} -> {ok, Conn, Results};                      
                {error, Reason} ->  
                        try exit(Conn, normal) of
                         _Any -> ok
                        catch
                         _E:_R -> ok
                        end,
                      lager:error("~p:sql_query_priv Connection To DB Failed, Reason: ~p~n", [?MODULE, {error, Reason}]),
                      {error, null, Reason}                            
            catch 
                Exception:Reason -> 
                        try exit(Conn, normal) of
                         _Any -> ok
                        catch
                         _E:_R -> ok
                        end,
                      lager:error("~p:sql_query_priv Connection To DB Failed, Exception:~p Reason: ~p~n", [?MODULE, Exception, Reason]),
                      {error, null, {Exception, Reason}} 
            end
    end. 

【讨论】:

  • 谢谢@Roman。我改变了我的实现,我的问题得到了解决。如果您好奇,请参阅上面的编辑。
【解决方案2】:

根据relevant documentationodbc 连接对于创建连接的Genserver 进程是私有的。

打开与数据库的连接。该连接与 创建它并且只能通过它访问的进程。这 函数可能会产生新的进程来处理连接。这些 如果创建连接的进程将终止 死了,或者如果你打电话给 disconnect/1。

您可以将其与可设置为privateETS 表进行比较,但对于ETS 表,您可以将它们设置为public。这对于odbc 连接是不可能的,因此您应该将您的代码移动到Genserver

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-27
    • 2012-10-13
    • 2016-01-20
    • 1970-01-01
    相关资源
    最近更新 更多