Skip to content

Commit

Permalink
chore: add backwards compat for Port.command impl
Browse files Browse the repository at this point in the history
  • Loading branch information
juanazam committed Feb 19, 2024
1 parent e2691b9 commit cdf129e
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 3 deletions.
29 changes: 26 additions & 3 deletions lib/statix/conn.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ defmodule Statix.Conn do

require Logger

otp_release = :erlang.system_info(:otp_release)
@otp_gte_26 otp_release >= ~c"26"

def new(host, port, prefix) when is_binary(host) do
new(String.to_charlist(host), port, prefix)
end
Expand Down Expand Up @@ -50,10 +53,30 @@ defmodule Statix.Conn do
defp transmit(packet, %__MODULE__{address: address, port: port, sock: sock_name}) do
sock = Process.whereis(sock_name)

if sock do
:gen_udp.send(sock, address, port, packet)
# The check below was implemented for backwards compatibility to avoid a performance issue on
# OTP on versiones older than 26. If the OTP version is 26 or above, we use gen_udp.send/4. If
# the version is older than that, we use the existing performance issue workaround.
if @otp_gte_26 do
if sock do
:gen_udp.send(sock, address, port, packet)
else
{:error, :port_closed}
end
else
{:error, :port_closed}
# This branch will only be executed on older version of OTP and will be eventually removed.
packet_with_header = Packet.add_header(packet, address, port)

try do
Port.command(sock, packet_with_header)
rescue
ArgumentError ->
{:error, :port_closed}
else
true ->
receive do
{:inet_reply, _port, status} -> status
end
end
end
end
end
32 changes: 32 additions & 0 deletions lib/statix/packet.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,38 @@
defmodule Statix.Packet do
@moduledoc false

import Bitwise

@doc """
Adds header to a built packet.
This is implemented to keep backwards compatibility with older OTP versions
(< 26). Will be eventually removed.
"""
def add_header(built_packet, {n1, n2, n3, n4}, port) do
true = Code.ensure_loaded?(:gen_udp)

anc_data_part =
if function_exported?(:gen_udp, :send, 5) do
[0, 0, 0, 0]
else
[]
end

header =
[
_addr_family = 1,
band(bsr(port, 8), 0xFF),
band(port, 0xFF),
band(n1, 0xFF),
band(n2, 0xFF),
band(n3, 0xFF),
band(n4, 0xFF)
] ++ anc_data_part

header_as_bytes = Enum.into(header, <<>>, fn byte -> <<byte>> end)
[header_as_bytes | built_packet]
end

def build(prefix, name, key, val, options) do
[prefix, key, ?:, val, ?|, metric_type(name)]
|> set_option(:sample_rate, options[:sample_rate])
Expand Down

0 comments on commit cdf129e

Please sign in to comment.