Skip to content

Commit

Permalink
Release 0.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
vkaa committed Dec 29, 2023
1 parent a6a08e8 commit ef658ff
Show file tree
Hide file tree
Showing 37 changed files with 1,888 additions and 48 deletions.
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# AspikeServerUmbrella

**TODO: Add description**
# Aerospike Server Emulator

*Uses Aerospike Binary Protocol* - https://github.com/vsavkov/aspike-protocol
2 changes: 1 addition & 1 deletion apps/aspike_server/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# AspikeServer

**TODO: Add description**
**Binary Aerospike Cluster protocol - Aerospike Server Emulator, in Erlang/Elixir**

## Installation

Expand Down
11 changes: 11 additions & 0 deletions apps/aspike_server/include/aspike_node_test.hrl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-define(TEST_NODE_NAME_0, "A0").
-define(TEST_ADDRESS, "127.0.0.1").
-define(TEST_PORT, 62169).

-define(TEST_USER1, <<"User1">>).
-define(TEST_PASSWORD1, "pass1").
-define(TEST_PASSWORD1_CRYPT, <<"$2a$10$7EqJtq98hPqEX7fNZaFWoOOY1Ba9.gZNwHJkrSKJl7mXQyPCsCrQa">>).
-define(TEST_SESSION_TTL, 12345).
-define(TEST_SESSION_TOKEN1, <<"test_session_token1">>).

-define(TEST_NAMESPACE, "Test-Namespace").
154 changes: 154 additions & 0 deletions apps/aspike_server/include/aspike_protocol.hrl
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
%% Proto header version
-define(AS_PROTO_VERSION, 2).

%% Proto message types
-define(AS_INFO_MESSAGE_TYPE, 1).
-define(AS_ADMIN_MESSAGE_TYPE, 2).
-define(AS_MESSAGE_TYPE, 3).
-define(AS_COMPRESSED_MESSAGE_TYPE, 4).
-define(PROTO_SIZE_MAX, (128 * 1024 * 1024)). % 2^27 = 128 MB

%% admin Commands
-define(LOGIN, 20). % 0x14 = 16#14.

%% Field IDs in admin Commands
-define(USER, 0).
-define(CREDENTIAL, 3).
-define(SESSION_TOKEN, 5).
-define(SESSION_TTL, 6).

-define(AS_PROTO_DISTANCE_BTW_SIZE_AND_FIELDS, 16).

%% Field IDs
-define(AS_FIELD_NAMESPACE, 0).
-define(AS_FIELD_SETNAME, 1).
-define(AS_FIELD_KEY, 2).
-define(AS_FIELD_DIGEST, 4).
-define(AS_FIELD_TASK_ID, 7).
-define(AS_FIELD_SOCKET_TIMEOUT, 9).
-define(AS_FIELD_RPS, 10).
-define(AS_FIELD_PID_ARRAY, 11).
-define(AS_FIELD_DIGEST_ARRAY, 12).
-define(AS_FIELD_MAX_RECORDS, 13).
-define(AS_FIELD_BVAL_ARRAY, 15).
-define(AS_FIELD_INDEX_RANGE, 22).
-define(AS_FIELD_INDEX_CONTEXT, 23).
-define(AS_FIELD_INDEX_TYPE, 26).
-define(AS_FIELD_UDF_PACKAGE_NAME, 30).
-define(AS_FIELD_UDF_FUNCTION, 31).
-define(AS_FIELD_UDF_ARGLIST, 32).
-define(AS_FIELD_UDF_OP, 33).
-define(AS_FIELD_QUERY_BINS, 40).
-define(AS_FIELD_BATCH_INDEX, 41).
-define(AS_FIELD_FILTER, 43).

%% Message info1 bits
-define(AS_MSG_INFO1_READ, (1 bsl 0)). % contains a read operation
-define(AS_MSG_INFO1_GET_ALL, (1 bsl 1)). % get all bins, period
-define(AS_MSG_INFO1_SHORT_QUERY, (1 bsl 2)). % short query
-define(AS_MSG_INFO1_BATCH_INDEX, (1 bsl 3)). % batch
-define(AS_MSG_INFO1_XDR, (1 bsl 4)). % operation is being performed by XDR
-define(AS_MSG_INFO1_GET_NOBINDATA, (1 bsl 5)). % do not get information about bins and its data
-define(AS_MSG_INFO1_READ_MODE_AP_ALL, (1 bsl 6)). % read mode all for AP namespaces.
-define(AS_MSG_INFO1_COMPRESS_RESPONSE, (1 bsl 7)). % tell server to compress it's response.

%% Message info2 bits
-define(AS_MSG_INFO2_WRITE, (1 bsl 0)). % contains a write semantic
-define(AS_MSG_INFO2_DELETE, (1 bsl 1)). % delete record
-define(AS_MSG_INFO2_GENERATION, (1 bsl 2)). % pay attention to the generation
-define(AS_MSG_INFO2_GENERATION_GT, (1 bsl 3)). % apply write if new generation >= old, good for restore
-define(AS_MSG_INFO2_DURABLE_DELETE, (1 bsl 4)). % transaction resulting in record deletion leaves tombstone (Enterprise only).
-define(AS_MSG_INFO2_CREATE_ONLY, (1 bsl 5)). % write record only if it doesn't exist
%% (Note: Bit 6 is unused.)
-define(AS_MSG_INFO2_RESPOND_ALL_OPS, (1 bsl 7)). % return a result for every operation.

%% Message info3 bits
-define(AS_MSG_INFO3_LAST, (1 bsl 0)). % this is the last of a multi-part message
-define(AS_MSG_INFO3_COMMIT_MASTER, (1 bsl 1)). % write commit level - bit 0
%% On send: Do not return partition done in scan/query.
%% On receive: Specified partition is done in scan/query.
-define(AS_MSG_INFO3_PARTITION_DONE, (1 bsl 2)).
-define(AS_MSG_INFO3_UPDATE_ONLY, (1 bsl 3)). % update existing record only, do not create new record
-define(AS_MSG_INFO3_CREATE_OR_REPLACE, (1 bsl 4)). % completely replace existing record, or create new record
-define(AS_MSG_INFO3_REPLACE_ONLY, (1 bsl 5)). % completely replace existing record, do not create new record
-define(AS_MSG_INFO3_SC_READ_TYPE, (1 bsl 6)). % see aerospike-client-c for details
-define(AS_MSG_INFO3_SC_READ_RELAX, (1 bsl 7)). % see aerospike-client-c for details


-define(AS_OPERATOR_READ, 1).
-define(AS_OPERATOR_WRITE, 2).
-define(AS_OPERATOR_CDT_READ, 3).
-define(AS_OPERATOR_CDT_MODIFY, 4).
-define(AS_OPERATOR_MAP_READ, 5).
-define(AS_OPERATOR_MAP_MODIFY, 6).
-define(AS_OPERATOR_INCR, 7).
-define(AS_OPERATOR_EXP_READ, 8).
-define(AS_OPERATOR_EXP_MODIFY, 9).
-define(AS_OPERATOR_APPEND, 10).
-define(AS_OPERATOR_PREPEND, 11).
-define(AS_OPERATOR_TOUCH, 12).
-define(AS_OPERATOR_BIT_READ, 13).
-define(AS_OPERATOR_BIT_MODIFY, 14).
-define(AS_OPERATOR_DELETE, 15).
-define(AS_OPERATOR_HLL_READ, 16).
-define(AS_OPERATOR_HLL_MODIFY, 17).

-define(KEY_DIGEST_SIZE, 160). % RIPEMD160 crypto:hash(ripemd160, ...)


-define(AS_BYTES_UNDEF, 0).
-define(AS_BYTES_INTEGER, 1).
-define(AS_BYTES_DOUBLE, 2).
-define(AS_BYTES_STRING, 3).
-define(AS_BYTES_BLOB, 4).
-define(AS_BYTES_JAVA, 7).
-define(AS_BYTES_CSHARP, 8).
-define(AS_BYTES_PYTHON, 9).
-define(AS_BYTES_RUBY, 10).
-define(AS_BYTES_PHP, 11).
-define(AS_BYTES_ERLANG, 12).
-define(AS_BYTES_BOOL, 17).
-define(AS_BYTES_HLL, 18).
-define(AS_BYTES_MAP, 19).
-define(AS_BYTES_LIST, 20).
-define(AS_BYTES_GEOJSON, 23).
-define(AS_BYTES_TYPE_MAX, 24).

%% as_proto reflects binary (physical) layout
-record(as_proto, {
version :: aspike:uint8_t(),
type :: aspike:uint8_t(),
sz :: aspike:uint48_t()
}).

%% as_msg reflects binary (physical) layout
-record(as_msg, {
header_sz :: aspike:uint8_t(), % number of uint8_ts in this header
info1 :: aspike:uint8_t(), % bitfield about this request
info2 :: aspike:uint8_t(),
info3 :: aspike:uint8_t(),
unused :: aspike:uint8_t(),
result_code :: aspike:uint8_t(),
generation :: aspike:uint32_t(),
record_ttl :: aspike:uint32_t(),
transaction_ttl :: aspike:uint32_t(),
n_fields :: aspike:uint16_t(), % size in uint8_ts
n_ops :: aspike:uint16_t() % number of operations
% followed by data that contain
% the fields, occupying n_fields bytes
% then the n_ops number of ops.
}).

%% aspike_message_type_header is a logical representation of as_msg
-record(aspike_message_type_header, {
result_code :: aspike:uint8_t(),
n_fields :: aspike:uint16_t(),
n_bins :: aspike:uint16_t(),
ttl :: aspike:uint32_t(),
timeout :: aspike:uint32_t(),
read_attr :: aspike:uint8_t(),
write_attr :: aspike:uint8_t(),
info_attr :: aspike:uint8_t(),
generation :: aspike:uint32_t(),
unused :: aspike:uint8_t()
}).
96 changes: 96 additions & 0 deletions apps/aspike_server/include/aspike_status.hrl
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
%% Corresponds to as_status.h

%% Success
-define(AEROSPIKE_OK, 0).

%% Server Errors
-define(AEROSPIKE_ERR_SERVER, 1).
-define(AEROSPIKE_ERR_RECORD_NOT_FOUND, 2).
-define(AEROSPIKE_ERR_RECORD_GENERATION, 3).
-define(AEROSPIKE_ERR_REQUEST_INVALID, 4).
-define(AEROSPIKE_ERR_RECORD_EXISTS, 5).
-define(AEROSPIKE_ERR_BIN_EXISTS, 6).
-define(AEROSPIKE_ERR_CLUSTER_CHANGE, 7).
-define(AEROSPIKE_ERR_SERVER_FULL, 8).
-define(AEROSPIKE_ERR_TIMEOUT, 9).
-define(AEROSPIKE_ERR_ALWAYS_FORBIDDEN, 10).
-define(AEROSPIKE_ERR_CLUSTER, 11).
-define(AEROSPIKE_ERR_BIN_INCOMPATIBLE_TYPE, 12).
-define(AEROSPIKE_ERR_RECORD_TOO_BIG, 13).
-define(AEROSPIKE_ERR_RECORD_BUSY, 14).
-define(AEROSPIKE_ERR_SCAN_ABORTED, 15).
-define(AEROSPIKE_ERR_UNSUPPORTED_FEATURE, 16).
-define(AEROSPIKE_ERR_BIN_NOT_FOUND, 17).
-define(AEROSPIKE_ERR_DEVICE_OVERLOAD, 18).
-define(AEROSPIKE_ERR_RECORD_KEY_MISMATCH, 19).
-define(AEROSPIKE_ERR_NAMESPACE_NOT_FOUND, 20).
-define(AEROSPIKE_ERR_BIN_NAME, 21).
-define(AEROSPIKE_ERR_FAIL_FORBIDDEN, 22).
-define(AEROSPIKE_ERR_FAIL_ELEMENT_NOT_FOUND, 23).
-define(AEROSPIKE_ERR_FAIL_ELEMENT_EXISTS, 24).
-define(AEROSPIKE_ERR_ENTERPRISE_ONLY, 25).
-define(AEROSPIKE_ERR_OP_NOT_APPLICABLE, 26).
-define(AEROSPIKE_FILTERED_OUT, 27).
-define(AEROSPIKE_LOST_CONFLICT, 28).
-define(AEROSPIKE_QUERY_END, 50).
-define(AEROSPIKE_SECURITY_NOT_SUPPORTED, 51).
-define(AEROSPIKE_SECURITY_NOT_ENABLED, 52).
-define(AEROSPIKE_SECURITY_SCHEME_NOT_SUPPORTED, 53).
-define(AEROSPIKE_INVALID_COMMAND, 54).
-define(AEROSPIKE_INVALID_FIELD, 55).
-define(AEROSPIKE_ILLEGAL_STATE, 56).
-define(AEROSPIKE_INVALID_USER, 60).
-define(AEROSPIKE_USER_ALREADY_EXISTS, 61).
-define(AEROSPIKE_INVALID_PASSWORD, 62).
-define(AEROSPIKE_EXPIRED_PASSWORD, 63).
-define(AEROSPIKE_FORBIDDEN_PASSWORD, 64).
-define(AEROSPIKE_INVALID_CREDENTIAL, 65).
-define(AEROSPIKE_EXPIRED_SESSION, 66).
-define(AEROSPIKE_INVALID_ROLE, 70).
-define(AEROSPIKE_ROLE_ALREADY_EXISTS, 71).
-define(AEROSPIKE_INVALID_PRIVILEGE, 72).
-define(AEROSPIKE_INVALID_WHITELIST, 73).
-define(AEROSPIKE_QUOTAS_NOT_ENABLED, 74).
-define(AEROSPIKE_INVALID_QUOTA, 75).
-define(AEROSPIKE_NOT_AUTHENTICATED, 80).
-define(AEROSPIKE_ROLE_VIOLATION, 81).
-define(AEROSPIKE_NOT_WHITELISTED, 82).
-define(AEROSPIKE_QUOTA_EXCEEDED, 83).
-define(AEROSPIKE_ERR_UDF, 100).
-define(AEROSPIKE_ERR_BATCH_DISABLED, 150).
-define(AEROSPIKE_ERR_BATCH_MAX_REQUESTS_EXCEEDED, 151).
-define(AEROSPIKE_ERR_BATCH_QUEUES_FULL, 152).
-define(AEROSPIKE_ERR_GEO_INVALID_GEOJSON, 160).
-define(AEROSPIKE_ERR_INDEX_FOUND, 200).
-define(AEROSPIKE_ERR_INDEX_NOT_FOUND, 201).
-define(AEROSPIKE_ERR_INDEX_OOM, 202).
-define(AEROSPIKE_ERR_INDEX_NOT_READABLE, 203).
-define(AEROSPIKE_ERR_INDEX, 204).
-define(AEROSPIKE_ERR_INDEX_NAME_MAXLEN, 205).
-define(AEROSPIKE_ERR_INDEX_MAXCOUNT, 206).
-define(AEROSPIKE_ERR_QUERY_ABORTED, 210).
-define(AEROSPIKE_ERR_QUERY_QUEUE_FULL, 211).
-define(AEROSPIKE_ERR_QUERY_TIMEOUT, 212).
-define(AEROSPIKE_ERR_QUERY, 213).
-define(AEROSPIKE_ERR_UDF_NOT_FOUND, 1301). % likely internal Aerospike server error?
-define(AEROSPIKE_ERR_LUA_FILE_NOT_FOUND, 1302). % likely internal Aerospike server error?

%% Client Errors.
%% These are C-client errors, irrelevant for Erlang client.
-define(AEROSPIKE_BATCH_FAILED, -16).
-define(AEROSPIKE_NO_RESPONSE, -15).
-define(AEROSPIKE_MAX_ERROR_RATE, -14).
-define(AEROSPIKE_USE_NORMAL_RETRY, -13).
-define(AEROSPIKE_ERR_MAX_RETRIES_EXCEEDED, -12).
-define(AEROSPIKE_ERR_ASYNC_QUEUE_FULL, -11).
-define(AEROSPIKE_ERR_CONNECTION, -10).
-define(AEROSPIKE_ERR_TLS_ERROR, -9).
-define(AEROSPIKE_ERR_INVALID_NODE, -8).
-define(AEROSPIKE_ERR_NO_MORE_CONNECTIONS, -7).
-define(AEROSPIKE_ERR_ASYNC_CONNECTION, -6).
-define(AEROSPIKE_ERR_CLIENT_ABORT, -5).
-define(AEROSPIKE_ERR_INVALID_HOST, -4).
-define(AEROSPIKE_NO_MORE_RECORDS, -3).
-define(AEROSPIKE_ERR_PARAM, -2).
-define(AEROSPIKE_ERR_CLIENT, -1).
-define(AEROSPIKE_ERR, -1).
20 changes: 20 additions & 0 deletions apps/aspike_server/lib/application.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
defmodule Aspike.Server.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false

use Application

@impl true
def start(_type, _args) do
children = [
{Task.Supervisor, name: Aspike.Server.ProcessorSupervisor},
{Aspike.Server, 4041}
]

# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: Aspike.Server.Supervisor]
Supervisor.start_link(children, opts)
end
end
37 changes: 24 additions & 13 deletions apps/aspike_server/lib/aspike_server.ex
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
defmodule AspikeServer do
@moduledoc """
Documentation for `AspikeServer`.
"""
defmodule Aspike.Server do
@moduledoc false
use Task, restart: :transient
require Logger

@doc """
Hello world.
## Examples
def start_link(arg) do
Task.start_link(__MODULE__, :accept, [arg])
end

iex> AspikeServer.hello()
:world
def accept(port) do
{:ok, socket} = :gen_tcp.listen(port,
[:binary,
packet: :raw,
active: false,
reuseaddr: true,
backlog: 4096])
Logger.info "Accepting connections on port #{port}"
loop_acceptor(socket)
end

"""
def hello do
:world
defp loop_acceptor(socket) do
{:ok, client} = :gen_tcp.accept(socket)
{:ok, pid} = Task.Supervisor.start_child(
Aspike.Server.ProcessorSupervisor,
Aspike.Server.Processor, :run, [client, <<>>])
:ok = :gen_tcp.controlling_process(client, pid)
loop_acceptor(socket)
end
end
Loading

0 comments on commit ef658ff

Please sign in to comment.