Melian - Perl client to the Melian cache
version 0.007
use Melian;
#-----------------------------------------------
# OO INTERFACE (easy, name-based)
#-----------------------------------------------
my $melian = Melian->new(
'dsn' => 'unix:///tmp/melian.sock',
);
my $row = $melian->fetch_by_string_from( 'cats', 'name', 'Pixel' );
# Using IDs directly (faster)
my $row2 = $melian->fetch_by_string( 1, 1, 'Pixel' );
#-----------------------------------------------
# FUNCTIONAL INTERFACE (fastest)
#-----------------------------------------------
use Melian qw(fetch_by_string_with);
my $conn = Melian->create_connection(
'dsn' => 'unix:///tmp/melian.sock',
);
# Must use IDs in functional mode
my $row3 = fetch_by_string_with( $conn, 1, 1, 'Pixel' );
Melian is a tiny, fast, no-nonsense Perl client for the Melian cache server.
Melian (the server) keeps full table snapshots in memory. Lookups are done entirely inside the server and returned as compact binary payloads. Think of it as a super-fast read-only lookup service.
This module gives you two ways to talk to Melian:
Use table names and column names:
$melian->fetch_by_string_from( 'cats', 'name', 'Pixel' );
Behind the scenes, the module:
- Looks up the table ID in the schema
- Looks up the column ID in the schema
- Builds the binary request to the server
- Reads the reply and decodes the binary row payload
This is the most convenient way. It is a little slower, because there is some name lookup and method dispatch each time.
Use this when you want raw speed:
my $conn = Melian->create_connection(...);
my $row = fetch_by_string_with( $conn, $table_id, $column_id, $key );
This avoids:
- Method calls
- Object construction
- Lookup of table IDs and column IDs
It is simply: "write a request to the socket, read a response."
If you're chasing microseconds, this is the mode for you.
Melian needs a schema so it knows which table IDs and column IDs correspond to which names. A schema looks something like:
people#0|60|id#0:int
Or:
people#0|60|id#0:int,cats#1|45|id#0:int;name#1:string
The format is simple:
table_name#table_id(multiple tables separated by,)|refresh_period_in_seconds|column_name#column_id:column_type(multiple columns separated by;)
You do NOT need to write this schema unless you want to. If you do not supply one, Melian will request it automatically from the server at startup.
If you provide a schema, it should match the schema set for the Melian server.
Once the client is constructed:
my $schema = $melian->schema();
Each table entry contains:
{
id => 1,
name => "cats",
period => 45,
indexes => [
{ id => 0, column => "id", type => "int" },
{ id => 1, column => "name", type => "string" },
],
}
If you use the functional API, you probably want to store them in constants:
use constant {
'CAT_ID_TABLE' => 1,
'CAT_ID_COLUMN' => 0, # integer column
'CAT_NAME_COLUMN' => 1, # string column
};
This saves name lookups on every request.
Fetch calls return a hashref keyed by column name with native values by default:
{
id => 42,
name => "Pixel",
}
If you need field types, use the *_with_fields methods to get:
{
id => { type => Melian::VALUE_INT64, value => 42 },
name => { type => Melian::VALUE_BYTES, value => "Pixel" },
}
Type mappings:
VALUE_NULL->undefVALUE_INT64-> Perl integerVALUE_FLOAT64-> Perl numberVALUE_DECIMAL-> ASCII stringVALUE_BOOL-> Perl boolean (0/1)VALUE_BYTES-> raw bytes; enabledecode_utf8to decode to UTF-8 strings
If decode_utf8 is enabled and a BYTES field contains invalid UTF-8, decoding
throws an exception.
my $melian = Melian->new(
'dsn' => 'unix:///tmp/melian.sock',
'timeout' => 1, # Only relevant for TCP/IP
'schema_spec' => 'people#0|60|id#0:int',
);
Creates a new client and automatically loads the schema.
You may specify:
-
schema— already-parsed schema hashrefmy $melian = Melian->new( 'schema' => { 'id' => 1, 'name' => 'cats', 'period' => 45, 'indexes' => [ { 'id' => 0, 'column' => "id", 'type' => 'int' }, { 'id' => 1, 'column' => "name", 'type' => 'string' }, ], } ... );You would normally either provide a spec, a file, or nothing (to let Melian fetch it from the server).
-
schema_spec— inline schema descriptionmy $melian = Melian->new( 'schema_spec' => 'cats#0|45|id#0:int;name#1:string', ... ); -
schema_file— path to JSON schema filemy $melian = Melian->new( 'schema_file' => '/etc/melian/schema.json', ... ); -
decode_utf8— decode BYTES values as UTF-8 strings (default false) -
nothing — Melian will ask the server for the schema
my $melian = Melian->new(...);
$melian->connect();
Opens the underlying socket. Called automatically by new().
$melian->disconnect();
Closes the socket. Called automatically when instance goes out of scope, so you don't need to think about this.
my $encoded_data = $melian->fetch_raw( 0, 0, pack 'V', 20 );
my $encoded_data = $melian->fetch_raw( 0, 1, 'Pixel' );
Fetches a raw binary payload. Does NOT decode it.
You probably don't want to use this. See fetch_by_int(),
fetch_by_int_from(), fetch_by_string(), and
fetch_by_string_from() instead.
my $encoded_data = $melian->fetch_raw_from( 'cats', 'id', pack 'V', 20 );
my $encoded_data = $melian->fetch_raw_from( 'cats', 'name', 'Pixel' );
Same as above, but uses names instead of IDs.
You probably don't want to use this. See fetch_by_int(),
fetch_by_int_from(), fetch_by_string(), and
fetch_by_string_from() instead.
my $hashref = $melian->fetch_by_string( 0, 1, 'Pixel' );
Fetches a binary row from the server and decodes into a Perl hashref with native values.
Same as fetch_by_string, but returns {type, value} pairs.
my $hashref = $melian->fetch_by_string( 'cats', 'name', 'Pixel' );
Name-based version. Slightly slower than using IDs.
Name-based version that returns {type, value} pairs.
my $hashref = $melian->fetch_by_int( 0, 0, 5 );
Same as fetch_by_string, but for integer-based column searches.
Same as fetch_by_int, but returns {type, value} pairs.
my $hashref = $melian->fetch_by_int_from( 'cats', 'id', 5 );
Name-based version. Slightly slower than using IDs.
Name-based version that returns {type, value} pairs.
These functions form the high-speed functional interface. They require a
connection hashref returned by create_connection().
my $conn = Melian->create_connection(%same_args_as_new);
Returns a connection hashref with a socket and options (including decode_utf8).
Same options as new(), but no object is created.
my $encoded_data = fetch_raw_with( $conn, 0, 0, pack 'V', 20 );
my $encoded_data = fetch_raw_with( $conn, 0, 1, 'Pixel' );
Similar to fetch_raw() but uses the connection object you get back from
create_connection().
You probably don't want to use this. See fetch_by_int_with() and
fetch_by_string_with() instead.
my $hashref = fetch_by_string_with( $conn, 0, 1, 'Pixel' );
Behaves like the corresponding OO method but skips object overhead and schema lookup.
Behaves like fetch_by_string_with, but returns {type, value} pairs.
my $hashref = fetch_by_int_with( $conn, 0, 0, 5 );
Behaves like the corresponding OO method but skips object overhead and schema lookup.
Behaves like fetch_by_int_with, but returns {type, value} pairs.
my $table_data = table_of( $schema, 'cats' );
my $table_id = $table_data->{'id'};
Fetches the table information from the schema.
my $table_data = table_of( $schema, 'cats' );
my $column_id = column_id_of( $table_data, 'name' );
Fetches the ID of a column from a given table metadata.
my $schema = load_schema_from_describe($conn);
This helps you retrieve the schema if you're using the functional interface. You can then use this schema to determine table and column IDs.
- OO mode is convenient but has overhead from name lookups and method calls.
- ID-based OO mode is faster because it skips name lookups.
- Functional mode is the fastest and is roughly equivalent to calling
syswriteandsysreaddirectly in Perl. - If you care about performance, use table and column IDs with the functional interface.
- Sawyer X
- Gonzalo Diethelm
This software is Copyright (c) 2025 by Sawyer X, Gonzalo Diethelm.
This is free software, licensed under:
The MIT (X11) License