Packages
appsignal
1.11.6
2.17.4
2.17.3
2.17.2
2.17.1
2.17.0
2.16.0
2.15.11
2.15.10
2.15.9
2.15.8
2.15.7
2.15.6
2.15.5
2.15.4
2.15.3
2.15.2
2.15.1
2.15.0
2.14.1
2.14.0
2.13.4
2.13.3
2.13.2
2.13.1
2.13.0
2.12.3
2.12.2
2.12.1
2.12.0
2.11.0
2.10.2
2.10.1
2.10.0
2.9.2
2.9.1
2.9.0
2.8.3
2.8.2
2.8.1
2.8.0
2.7.13
2.7.12
2.7.11
2.7.10
2.7.9
2.7.8
2.7.7
2.7.6
2.7.5
2.7.4
2.7.3
2.7.2
2.7.1
2.7.0
2.7.0-beta.1
2.6.1
2.6.0
2.5.3
2.5.2
2.5.1
2.5.0
2.4.3
2.4.2
2.4.1
2.4.0
2.3.1
2.3.0
2.2.19
2.2.18
2.2.17
2.2.16
2.2.15
2.2.14
2.2.13
2.2.12
retired
2.2.11
2.2.10
2.2.9
2.2.8
2.2.7
2.2.6
2.2.5
2.2.4
2.2.3
2.2.2
2.2.1
2.2.0
2.1.15
2.1.14
2.1.13
2.1.12
2.1.11
2.1.10
2.1.9
2.1.8
2.1.7
2.1.7-beta.1
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.8
2.0.7
2.0.6
2.0.6-beta.1
2.0.5
retired
2.0.4
retired
2.0.3
retired
2.0.2
retired
2.0.1
retired
2.0.0
retired
2.0.0-beta.11
2.0.0-beta.10
2.0.0-beta.9
2.0.0-beta.8
2.0.0-beta.7
2.0.0-beta.6
2.0.0-beta.5
2.0.0-beta.4
2.0.0-beta.3
2.0.0-beta.2
2.0.0-beta.1
2.0.0-alpha.7
2.0.0-alpha.6
2.0.0-alpha.5
2.0.0-alpha.4
2.0.0-alpha.3
2.0.0-alpha.2
2.0.0-alpha.1
1.13.5
1.13.4
1.13.3
1.13.2
1.13.1
1.13.0
1.13.0-beta.2
1.13.0-beta.1
1.12.1
1.12.0
1.12.0-beta.3
1.12.0-beta.2
1.12.0-beta.1
1.11.8
1.11.7
1.11.6
1.11.5
1.11.4
1.11.3
1.11.2
1.11.1
1.11.0
1.11.0-beta.1
1.10.13
1.10.12
1.10.11
1.10.10
1.10.9
1.10.8
1.10.7
1.10.6
1.10.5
1.10.4
1.10.3
1.10.2
1.10.1
1.10.0
1.9.4
1.9.3
1.9.2
1.9.1
1.9.1-beta.1
1.9.0
1.9.0-beta.1
1.9.0-alpha.1
1.8.2
1.8.1
1.8.1-beta.2
1.8.1-beta.1
1.8.0
1.7.2
1.7.1
1.7.0
1.7.0-beta.2
1.7.0-beta.1
1.7.0-alpha.4
1.7.0-alpha.3
1.7.0-alpha.2
1.7.0-alpha.1
1.6.7
1.6.6
1.6.6-beta.2
1.6.6-beta.1
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.6.0-beta.1
1.6.0-alpha.1
1.5.1
1.5.0
1.5.0-beta.2
1.5.0-beta.1
1.4.11-beta.1
1.4.10
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.4.0-alpha.1
1.3.6
1.3.6-beta.1
1.3.5
1.3.4
1.3.4-beta.1
1.3.3
1.3.2
1.3.1
1.3.0
1.3.0-beta.3
1.3.0-beta.2
1.3.0-beta.1
1.2.3
1.2.2
1.2.1
1.2.0
1.1.1
1.1.0
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
1.0.0-rc.1
0.13.0
0.12.3
0.12.2
0.12.1
0.12.0
0.11.6
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.2
0.9.1
0.9.0
0.8.0
0.7.1
0.7.0
0.6.2
0.6.1
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
0.0.9
0.0.8
0.0.7
0.0.6
0.0.5
0.0.4
0.0.3
0.0.2
0.0.1
Collects error and performance data from your Elixir applications and sends it to AppSignal
Current section
Files
Jump to
Current section
Files
lib/appsignal/transaction/registry.ex
defmodule Appsignal.TransactionRegistry do
@moduledoc """
Internal module which keeps a registry of the transaction handles
linked to their originating process.
This is used on various places to link a calling process to its transaction.
For instance, the `Appsignal.ErrorHandler` module uses it to be able to
complete the transaction in case the originating process crashed.
The transactions are stored in an ETS table (with
`{:write_concurrency, true}`, so no bottleneck is created); and the
originating process is monitored to clean up the ETS table when the
process has finished.
"""
require Logger
alias Appsignal.{Config, Transaction}
alias Appsignal.Transaction.{ETS, Receiver}
@doc """
Register the current process as the owner of the given transaction.
"""
@spec register(Transaction.t()) :: :ok
def register(transaction) do
if Config.active?() && receiver_alive?() do
pid = self()
true = ETS.insert({pid, transaction, Receiver.monitor(pid)})
:ok
end
end
@doc """
Given a process ID, return its associated transaction.
"""
@spec lookup(pid) :: Transaction.t() | nil
def lookup(pid) do
case Config.active?() && receiver_alive?() && ETS.lookup(pid) do
[{^pid, %Transaction{} = transaction, _}] -> transaction
[{^pid, %Transaction{} = transaction}] -> transaction
[{^pid, :ignore}] -> :ignored
_ -> nil
end
end
@doc false
@spec lookup(pid, boolean) :: Transaction.t() | nil | :removed
def lookup(pid, return_removed) do
IO.warn(
"Appsignal.TransactionRegistry.lookup/2 is deprecated. Use Appsignal.TransactionRegistry.lookup/1 instead"
)
case receiver_alive?() && ETS.lookup(pid) do
[{^pid, :removed}] ->
case return_removed do
false -> nil
true -> :removed
end
[{^pid, transaction, _}] ->
transaction
[{^pid, transaction}] ->
transaction
false ->
nil
[] ->
nil
end
end
@doc """
Unregister the current process as the owner of the given transaction.
"""
@spec remove_transaction(Transaction.t()) :: :ok | {:error, :not_found} | {:error, :no_receiver}
def remove_transaction(%Transaction{} = transaction) do
if receiver_alive?() do
transaction
|> pids_and_monitor_references()
|> Receiver.demonitor()
remove(transaction)
else
{:error, :no_receiver}
end
end
defp remove(transaction) do
case pids_and_monitor_references(transaction) do
[[_pid, _reference] | _] = pids_and_refs ->
delete(pids_and_refs)
[[_pid] | _] = pids ->
delete(pids)
[] ->
{:error, :not_found}
end
end
@doc """
Ignore a process in the error handler.
"""
@spec ignore(pid()) :: :ok
def ignore(pid) do
if receiver_alive?() do
ETS.insert({pid, :ignore})
:ok
else
{:error, :no_receiver}
end
end
@doc """
Check if a progress is ignored.
"""
@deprecated "Use Appsignal.TransactionRegistry.lookup/1 instead."
@spec ignored?(pid()) :: boolean()
def ignored?(pid) do
case receiver_alive?() && ETS.lookup(pid) do
[{^pid, :ignore}] -> true
_ -> false
end
end
defp delete([[pid, _] | tail]) do
ETS.delete(pid)
delete(tail)
end
defp delete([[pid] | tail]) do
ETS.delete(pid)
delete(tail)
end
defp delete([]), do: :ok
defp receiver_alive? do
pid = Process.whereis(Receiver)
!is_nil(pid) && Process.alive?(pid)
end
def pids_and_monitor_references(transaction) do
ETS.match({:"$1", transaction, :"$2"}) ++ ETS.match({:"$1", transaction})
end
end