Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
0.2.1 (February 24, 2025):
1.0.0 (February 25, 2025):
- Fixed the SDK language version to correctly reflect the package version when it's installed from hex.pm.
- BREAKING CHANGES:
- Renamed the `:socket_path` option to `:address` in `Split.Supervisor.start_link/1`.

0.2.0 (February 14, 2025):
- Added new variations of the get treatment functions to support evaluating flags in given flag set/s: `Split.get_treatments_by_flag_set/3`, `Split.get_treatments_by_flag_sets/3`, `Split.get_treatments_with_config_by_flag_set/3`, and `Split.get_treatments_with_config_by_flag_sets/3`.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Split SDK for Elixir

[![hex.pm version](https://img.shields.io/hexpm/v/split_thin_sdk)](https://img.shields.io/hexpm/v/split_thin_sdk) [![Build Status](https://github.com/splitio/elixir-thin-client/actions/workflows/ci-cd.yml/badge.svg)](https://github.com/splitio/elixir-thin-client/actions/workflows/ci-cd.yml) [![Greenkeeper badge](https://badges.greenkeeper.io/splitio/elixir-thin-client.svg)](https://greenkeeper.io/)
[![hex.pm version](https://img.shields.io/hexpm/v/split_thin_sdk)](https://img.shields.io/hexpm/v/split_thin_sdk) [![Build Status](https://github.com/splitio/elixir-thin-client/actions/workflows/ci.yml/badge.svg)](https://github.com/splitio/elixir-thin-client/actions/workflows/ci-cd.yml) [![Greenkeeper badge](https://badges.greenkeeper.io/splitio/elixir-thin-client.svg)](https://greenkeeper.io/)

## Overview
This SDK is designed to work with Split, the platform for controlled rollouts, which serves features to your users via feature flags to manage your complete customer experience.
Expand Down Expand Up @@ -36,7 +36,7 @@ Below is a simple example that describes the instantiation and most basic usage

```elixir
# Start the SDK supervisor
Split.Supervisor.start_link(socket_path: "/var/run/splitd.sock")
Split.Supervisor.start_link(address: "/var/run/splitd.sock")

# Get treatment for a user
case Split.get_treatment(user_id, feature_flag_name) do
Expand Down
6 changes: 3 additions & 3 deletions lib/split.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ defmodule Split do
def start(_type, _args) do
children = [
# ... other children ...
{Split, [socket_path: "/var/run/split.sock"]}
{Split, [address: "/var/run/split.sock"]}
]

opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Expand All @@ -32,7 +32,7 @@ defmodule Split do

`Split` takes a number of keyword arguments as options when starting. The following options are available:

- `:socket_path`: **OPTIONAL** The path to the splitd socket file. Default is `"/var/run/splitd.sock"`.
- `:address`: **OPTIONAL** The path to the splitd socket file. Default is `"/var/run/splitd.sock"`.
- `:pool_size`: **OPTIONAL** The size of the pool of connections to the splitd daemon. Default is the number of online schedulers in the Erlang VM (See: https://www.erlang.org/doc/apps/erts/erl_cmd.html).
- `:connect_timeout`: **OPTIONAL** The timeout in milliseconds to connect to the splitd daemon. Default is `1000`.

Expand All @@ -52,7 +52,7 @@ defmodule Split do

@typedoc "An option that can be provided when starting `Split`. See [options](#module-options) for more information."
@type option ::
{:socket_path, String.t()}
{:address, String.t()}
| {:pool_size, non_neg_integer()}
| {:connect_timeout, non_neg_integer()}

Expand Down
4 changes: 2 additions & 2 deletions lib/split/rpc/message.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule Split.RPC.Message do
use Split.RPC.Opcodes

@protocol_version 0x01
@client_id "Splitd_Elixir-0.2.1-rc.0"
@client_id "Splitd_Elixir-1.0.0"

@type opcode :: unquote(Enum.reduce(@opcodes, &{:|, [], [&1, &2]}))
@type protocol_version :: unquote(@protocol_version)
Expand Down Expand Up @@ -37,7 +37,7 @@ defmodule Split.RPC.Message do
## Examples

iex> Message.register()
%Message{v: 1, o: 0, a: ["123", "Splitd_Elixir-0.2.1-rc.0", 1]}
%Message{v: 1, o: 0, a: ["123", "Splitd_Elixir-1.0.0", 1]}
"""
@spec register() :: t()
def register, do: %__MODULE__{o: @register_opcode, a: ["123", @client_id, 1]}
Expand Down
14 changes: 7 additions & 7 deletions lib/split/sockets/conn.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ defmodule Split.Sockets.Conn do

@type t :: %__MODULE__{
socket: port() | nil,
socket_path: String.t(),
address: String.t(),
opts: keyword()
}

defstruct [
:socket,
:socket_path,
:address,
:opts
]

Expand All @@ -31,20 +31,20 @@ defmodule Split.Sockets.Conn do
@default_rcv_timeout 1000

@spec new(String.t(), keyword()) :: t
def new(socket_path, opts \\ []) do
def new(address, opts \\ []) do
%__MODULE__{
socket: nil,
socket_path: socket_path,
address: address,
opts: opts
}
end

@spec connect(t) :: {:ok, t()} | {:error, t(), term()}
def connect(%__MODULE__{socket: nil, socket_path: socket_path, opts: opts} = conn) do
def connect(%__MODULE__{socket: nil, address: address, opts: opts} = conn) do
connect_timeout = Keyword.get(opts, :connect_timeout, @default_connect_timeout)

Telemetry.span(:connect, %{socket_path: socket_path, pool_name: opts[:pool_name]}, fn ->
case :gen_tcp.connect({:local, socket_path}, 0, @connect_opts, connect_timeout) do
Telemetry.span(:connect, %{address: address, pool_name: opts[:pool_name]}, fn ->
case :gen_tcp.connect({:local, address}, 0, @connect_opts, connect_timeout) do
{:ok, socket} ->
conn = %{conn | socket: socket}

Expand Down
12 changes: 6 additions & 6 deletions lib/split/sockets/pool.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ defmodule Split.Sockets.Pool do
end

def start_link(opts) do
socket_path = Keyword.get(opts, :socket_path, "/var/run/splitd.sock")
address = Keyword.get(opts, :address, "/var/run/splitd.sock")
pool_name = Keyword.get(opts, :pool_name, __MODULE__)
pool_size = Keyword.get(opts, :pool_size, System.schedulers_online())

opts =
opts
|> Keyword.put_new(:socket_path, socket_path)
|> Keyword.put_new(:address, address)
|> Keyword.put_new(:pool_size, pool_size)
|> Keyword.put_new(:pool_name, pool_name)

Expand Down Expand Up @@ -100,11 +100,11 @@ defmodule Split.Sockets.Pool do

@impl NimblePool
def init_pool(opts) do
socket_path = Keyword.get(opts, :socket_path)
address = Keyword.get(opts, :address)

unless File.exists?(socket_path) do
unless File.exists?(address) do
Logger.error("""
The Split Daemon (splitd) socket was not found at #{socket_path}.
The Split Daemon (splitd) socket was not found at address #{address}.

This is likely because the Splitd daemon is not running.
""")
Expand All @@ -117,7 +117,7 @@ defmodule Split.Sockets.Pool do

@impl NimblePool
def init_worker({opts, _metrics_ref} = pool_state) do
{:ok, Conn.new(Keyword.get(opts, :socket_path), opts), pool_state}
{:ok, Conn.new(Keyword.get(opts, :address), opts), pool_state}
end

@impl NimblePool
Expand Down
4 changes: 2 additions & 2 deletions lib/split/telemetry.ex
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ defmodule Split.Telemetry do

#### Metadata

* `socket_path` - The path to the socket file.
* `address` - The path to the socket file.
* `pool_name` - The name of the pool being used.

### Connect Stop
Expand All @@ -114,7 +114,7 @@ defmodule Split.Telemetry do

#### Metadata

* `socket_path` - The path to the socket file.
* `address` - The path to the socket file.
* `pool_name` - The name of the pool being used.
* `error` - The error message if the connection fails.

Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule SplitThinElixir.MixProject do
def project do
[
app: :split,
version: "0.2.1-rc.0",
version: "1.0.0",
elixir: "~> 1.14",
elixirc_paths: elixirc_paths(Mix.env()),
start_permanent: Mix.env() == :prod,
Expand Down
40 changes: 20 additions & 20 deletions test/sockets/conn_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,34 @@ defmodule Split.Sockets.ConnTest do
describe "telemetry events" do
setup context do
test_id = :erlang.phash2(context.test)
socket_path = "/tmp/test-splitd-#{test_id}.sock"
address = "/tmp/test-splitd-#{test_id}.sock"
process_name = :"test-#{test_id}"

start_supervised!(
{Split.Test.MockSplitdServer, socket_path: socket_path, name: process_name},
{Split.Test.MockSplitdServer, address: address, name: process_name},
id: process_name,
restart: :transient
)

Split.Test.MockSplitdServer.wait_until_listening(socket_path)
Split.Test.MockSplitdServer.wait_until_listening(address)

{:ok, socket_path: socket_path, splitd_name: process_name}
{:ok, address: address, splitd_name: process_name}
end

test "emits telemetry events for successful connection", %{socket_path: socket_path} do
test "emits telemetry events for successful connection", %{address: address} do
ref =
:telemetry_test.attach_event_handlers(self(), [
[:split, :connect, :start],
[:split, :connect, :stop]
])

Conn.new(socket_path) |> Conn.connect()
Conn.new(address) |> Conn.connect()

assert_received {[:split, :connect, :start], ^ref, _, %{socket_path: ^socket_path}}
assert_received {[:split, :connect, :start], ^ref, _, %{address: ^address}}
assert_received {[:split, :connect, :stop], ^ref, _, %{}}
end

test "emits telemetry events for registration message on connect", %{socket_path: socket_path} do
test "emits telemetry events for registration message on connect", %{address: address} do
ref =
:telemetry_test.attach_event_handlers(self(), [
[:split, :send, :start],
Expand All @@ -45,7 +45,7 @@ defmodule Split.Sockets.ConnTest do
[:split, :receive, :stop]
])

Conn.new(socket_path) |> Conn.connect()
Conn.new(address) |> Conn.connect()

assert_received {[:split, :send, :start], ^ref, _,
%{request: %Message{v: 1, o: @register_opcode}}}
Expand All @@ -59,7 +59,7 @@ defmodule Split.Sockets.ConnTest do
end

test "emits telemetry events for failed connection", %{
socket_path: socket_path,
address: address,
splitd_name: splitd_name
} do
ref =
Expand All @@ -71,21 +71,21 @@ defmodule Split.Sockets.ConnTest do
# Stop the mocked splitd socket to receive connection errors
:ok = stop_supervised(splitd_name)

assert {:error, _conn, reason} = Conn.new(socket_path) |> Conn.connect()
assert {:error, _conn, reason} = Conn.new(address) |> Conn.connect()

assert_received {[:split, :connect, :start], ^ref, _, %{socket_path: ^socket_path}}
assert_received {[:split, :connect, :start], ^ref, _, %{address: ^address}}

assert_received {[:split, :connect, :stop], ^ref, _, %{error: ^reason}}
end

test "emits telemetry events for successful message sending", %{socket_path: socket_path} do
test "emits telemetry events for successful message sending", %{address: address} do
ref =
:telemetry_test.attach_event_handlers(self(), [
[:split, :send, :start],
[:split, :send, :stop]
])

{:ok, conn} = Conn.new(socket_path) |> Conn.connect()
{:ok, conn} = Conn.new(address) |> Conn.connect()

message = Message.get_treatment(key: "user-id", feature_name: "feature")

Expand All @@ -97,7 +97,7 @@ defmodule Split.Sockets.ConnTest do
end

test "emits telemetry events for failed message sending", %{
socket_path: socket_path,
address: address,
splitd_name: splitd_name
} do
ref =
Expand All @@ -106,7 +106,7 @@ defmodule Split.Sockets.ConnTest do
[:split, :send, :stop]
])

{:ok, conn} = Conn.new(socket_path) |> Conn.connect()
{:ok, conn} = Conn.new(address) |> Conn.connect()

message = Message.get_treatment(key: "user-id", feature_name: "feature")

Expand All @@ -120,14 +120,14 @@ defmodule Split.Sockets.ConnTest do
assert_received {[:split, :send, :stop], ^ref, _, %{error: ^reason}}
end

test "emits telemetry events for successful message receiving", %{socket_path: socket_path} do
test "emits telemetry events for successful message receiving", %{address: address} do
ref =
:telemetry_test.attach_event_handlers(self(), [
[:split, :receive, :start],
[:split, :receive, :stop]
])

{:ok, conn} = Conn.new(socket_path) |> Conn.connect()
{:ok, conn} = Conn.new(address) |> Conn.connect()

message = Message.get_treatment(key: "user-id", feature_name: "feature")

Expand All @@ -138,14 +138,14 @@ defmodule Split.Sockets.ConnTest do
assert_received {[:split, :receive, :stop], ^ref, _, %{response: ^response}}
end

test "emits telemetry events for failed message receiving", %{socket_path: socket_path} do
test "emits telemetry events for failed message receiving", %{address: address} do
ref =
:telemetry_test.attach_event_handlers(self(), [
[:split, :receive, :start],
[:split, :receive, :stop]
])

{:ok, conn} = Conn.new(socket_path) |> Conn.connect()
{:ok, conn} = Conn.new(address) |> Conn.connect()

# receive the registration messages
assert_received {[:split, :receive, :start], ^ref, _, %{}}
Expand Down
10 changes: 4 additions & 6 deletions test/sockets/pool_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,13 @@ defmodule Split.Sockets.PoolTest do

setup_all context do
test_id = :erlang.phash2(context.module)
socket_path = "/tmp/test-splitd-#{test_id}.sock"
address = "/tmp/test-splitd-#{test_id}.sock"

start_supervised!(
{Split.Test.MockSplitdServer, socket_path: socket_path, name: :"test-#{test_id}"}
)
start_supervised!({Split.Test.MockSplitdServer, address: address, name: :"test-#{test_id}"})

Split.Test.MockSplitdServer.wait_until_listening(socket_path)
Split.Test.MockSplitdServer.wait_until_listening(address)

start_supervised!({Split, socket_path: socket_path, pool_name: __MODULE__, pool_size: 10})
start_supervised!({Split, address: address, pool_name: __MODULE__, pool_size: 10})

:ok
end
Expand Down
10 changes: 4 additions & 6 deletions test/split_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@ defmodule SplitThinElixirTest do

setup_all context do
test_id = :erlang.phash2(context.module)
socket_path = "/tmp/test-splitd-#{test_id}.sock"
address = "/tmp/test-splitd-#{test_id}.sock"

start_supervised!(
{Split.Test.MockSplitdServer, socket_path: socket_path, name: :"test-#{test_id}"}
)
start_supervised!({Split.Test.MockSplitdServer, address: address, name: :"test-#{test_id}"})

Split.Test.MockSplitdServer.wait_until_listening(socket_path)
Split.Test.MockSplitdServer.wait_until_listening(address)

start_supervised!({Split, socket_path: socket_path})
start_supervised!({Split, address: address})

:ok
end
Expand Down
Loading
Loading