AmberDB
view release on metacpan or search on metacpan
xt/amberdb_ramdisk.t view on Meta::CPAN
database => 'plain_db',
path => { dbase_dir => $t_root, ramdisk_dir => $plain_ram }
);
is( $plain_db->config('ramdisk_mounted'), 0, 'Plain local dir config ramdisk_mounted is 0' );
my $plain_info = $plain_db->ramdisk_setup();
is( $plain_info->{is_mounted}, 0, 'Plain local directory is correctly identified as unmounted (0)' );
is( $plain_info->{mount_desc}, 'Local Storage (No RAM-disk active)', 'mount_desc shows local storage' );
}
# Test direct RAM-disk path diagnostics
{
local $ENV{AMBERDB_TEST_RAMDISK} = 0;
my $direct_db = AmberDB->new(
database => 'test',
path => { dbase_dir => $t_root, ramdisk_dir => ($^O eq 'MSWin32' || $^O eq 'msys' || $^O eq 'cygwin') ? 'R:/amberdb_test' : '/dev/shm/amberdb_test' }
);
my $d_info = $direct_db->ramdisk_setup();
ok( defined $d_info->{is_mounted}, 'Direct RAM path diagnostics evaluated' );
}
};
subtest 'Global use_ramdisk => 1 and 2 configuration' => sub {
plan tests => 13;
my $g_tmp = tempdir( CLEANUP => 1 );
my $g_mount = tempdir( CLEANUP => 1 );
my $g_ram = File::Spec->catdir( $g_mount, 'amberdb_catalog' );
make_path($g_ram);
# 1. First, create database schema and records on permanent disk without RAM-disk
my $schema_dir = File::Spec->catdir( $g_tmp, 'schema' );
make_path($schema_dir);
# Write schema files
{
open my $fh1, '>', File::Spec->catfile( $schema_dir, 'catalog_books.table' );
print $fh1 "{ record_index => 1, search_block => [1] };\n";
close $fh1;
open my $fh2, '>', File::Spec->catfile( $schema_dir, 'catalog_authors.table' );
print $fh2 "{ record_index => 1 };\n";
close $fh2;
open my $fh3, '>', File::Spec->catfile( $schema_dir, 'catalog_excluded.table' );
print $fh3 "{ record_index => 1, use_ramdisk => 0 };\n";
close $fh3;
}
{
local $ENV{AMBERDB_TEST_RAMDISK} = 0;
my $init_db = AmberDB->new(
database => 'catalog',
path => { dbase_dir => $g_tmp }
);
$init_db->insert_id( 'catalog_books', 1, 'Book One' );
$init_db->insert_id( 'catalog_authors', 10, 'Author Ten' );
$init_db->insert_id( 'catalog_excluded', 99, 'Excluded Data' );
}
# 2. Open with global cfg => { use_ramdisk => 1 }
my $adb_g1 = AmberDB->new(
database => 'catalog',
cfg => { use_ramdisk => 1 },
path => { dbase_dir => $g_tmp, ramdisk_dir => $g_ram }
);
# Access catalog_books via table_path
my $b_path = $adb_g1->table_path('catalog_books');
my $b_ram = $adb_g1->ramdisk_path('catalog_books');
ok( -e "$b_ram.inx", 'Global use_ramdisk => 1 preloads .inx to RAM-disk on table_path' );
ok( !-e "$b_ram.db", 'Global use_ramdisk => 1 does NOT copy .db (data remains on disk)' );
is( ($adb_g1->read_id('catalog_books', 1))[1], 'Book One', 'read_id works seamlessly' );
# Access catalog_authors via table_info
my $a_info = $adb_g1->table_info('catalog_authors');
my $a_ram = $adb_g1->ramdisk_path('catalog_authors');
is( $a_info->{use_ramdisk}, 1, 'Table inherits global use_ramdisk => 1' );
ok( -e "$a_ram.inx", 'Preloads .inx on table_info call' );
# Check excluded table with explicit use_ramdisk => 0
my $e_info = $adb_g1->table_info('catalog_excluded');
my $e_ram = $adb_g1->ramdisk_path('catalog_excluded');
is( $e_info->{use_ramdisk}, 0, 'Table preserves explicit use_ramdisk => 0 override' );
ok( !-e "$e_ram.inx", 'Excluded table is NOT copied to RAM-disk' );
# 3. Open with global cfg => { use_ramdisk => 2 } (Full Table Acceleration)
my $adb_g2 = AmberDB->new(
database => 'catalog',
cfg => { use_ramdisk => 2 },
path => { dbase_dir => $g_tmp, ramdisk_dir => $g_ram }
);
my $b2_path = $adb_g2->table_path('catalog_books');
ok( -e "$b_ram.db", 'Global use_ramdisk => 2 preloads .db to RAM-disk on table_path' );
ok( -e "$b_ram.inx", 'Global use_ramdisk => 2 keeps .inx on RAM-disk' );
is( ($adb_g2->read_id('catalog_books', 1))[1], 'Book One', 'read_id reads from RAM-disk in mode 2' );
# Insert new record under global mode 2
$adb_g2->insert_id( 'catalog_books', 2, 'Book Two' );
is( ($adb_g2->read_id('catalog_books', 2))[1], 'Book Two', 'Newly inserted record readable' );
ok( -e "$b_ram.db", 'RAM-disk .db persists with new record' );
ok( -e "$b_path.db", 'Permanent disk .db dual-written synchronously' );
};
subtest 'Unmounted RAM-disk strips use_ramdisk from table_info and table_attr' => sub {
plan tests => 6;
my $u_tmp = tempdir( CLEANUP => 1 );
my $schema_dir = File::Spec->catdir( $u_tmp, 'schema' );
make_path($schema_dir);
{
open my $fh, '>', File::Spec->catfile( $schema_dir, 'books_rd.table' );
print $fh "{ record_index => 1, use_ramdisk => 1 };\n";
close $fh;
}
my $u_mount = File::Temp->newdir( CLEANUP => 1 );
my $u_ram = File::Spec->catdir( $u_mount, 'amberdb_unmount_test' );
make_path($u_ram);
# Ensure unmounted state
local $ENV{AMBERDB_TEST_RAMDISK} = 0;
my $adb_unmounted = AmberDB->new(
database => 'unmount_test',
cfg => { use_ramdisk => 1 },
path => { dbase_dir => $u_tmp, ramdisk_dir => $u_ram }
);
# 1. From schema file
my $file_info = $adb_unmounted->table_info('books_rd');
ok( !exists $file_info->{use_ramdisk}, 'use_ramdisk stripped from schema file when RAM-disk is unmounted' );
# 2. From table_attr
$adb_unmounted->table_attr( 'dynamic_tbl', use_ramdisk => 1 );
my $attr_info = $adb_unmounted->table_info('dynamic_tbl');
ok( !exists $attr_info->{use_ramdisk}, 'use_ramdisk stripped from table_info after table_attr when unmounted' );
is( $adb_unmounted->table_attr( 'dynamic_tbl', 'use_ramdisk' ), undef, 'table_attr getter returns undef when unmounted' );
# 3. From global config inheritance
my $glob_info = $adb_unmounted->table_info('unconfigured_tbl');
ok( !exists $glob_info->{use_ramdisk}, 'Global use_ramdisk is NOT inherited when unmounted' );
# 4. Now simulate mounted RAM-disk
local $ENV{AMBERDB_TEST_RAMDISK} = 1;
my $adb_mounted = AmberDB->new(
database => 'unmount_test',
cfg => { use_ramdisk => 1 },
path => { dbase_dir => $u_tmp, ramdisk_dir => $u_ram }
);
my $m_file_info = $adb_mounted->table_info('books_rd');
is( $m_file_info->{use_ramdisk}, 1, 'use_ramdisk preserved when RAM-disk is mounted' );
$adb_mounted->table_attr( 'dyn_m_tbl', use_ramdisk => 1 );
is( $adb_mounted->table_attr( 'dyn_m_tbl', 'use_ramdisk' ), 1, 'table_attr preserves use_ramdisk when mounted' );
};
subtest 'use_ramdisk => 3 (Volatile RAM-Disk Tier 3 & TTL)' => sub {
my $tmp = File::Temp->newdir( CLEANUP => 1 );
my $t3_mount = File::Temp->newdir( CLEANUP => 1 );
my $t3_ram = File::Spec->catdir( $t3_mount, 'amberdb_session_db' );
make_path($t3_ram);
local $ENV{AMBERDB_TEST_RAMDISK} = 1;
# 1. Global use_ramdisk => 3 rejection/fallback to 0
my $adb_global = AmberDB->new(
database => 'session_db',
cfg => { use_ramdisk => 3 },
path => { dbase_dir => $tmp, ramdisk_dir => $t3_ram }
);
is( $adb_global->config('use_ramdisk'), 0, 'Global use_ramdisk => 3 in constructor falls back to 0' );
$adb_global->config( use_ramdisk => 3 );
is( $adb_global->config('use_ramdisk'), 0, 'Global config(use_ramdisk => 3) falls back to 0' );
my $inh_info = $adb_global->table_info('unspec_table');
is( $inh_info->{use_ramdisk}, 0, 'Unspecified table inherits 0, not 3' );
# 2. Per-table use_ramdisk => 3 configuration and schema stripping
my $adb = AmberDB->new(
database => 'session_db',
path => { dbase_dir => $tmp, ramdisk_dir => $t3_ram }
);
my $ramdisk_dir = $adb->ramdisk_dir();
$adb->table_attr( 'session_store',
use_ramdisk => 3,
search_block => [1],
record_index => 1,
match_block => [2],
);
my $s_info = $adb->table_info('session_store');
is( $s_info->{use_ramdisk}, 3, 'Table configured with use_ramdisk => 3' );
is( $s_info->{use_simple}, 1, 'Simple mode enforced for Tier 3' );
is( $s_info->{ramdisk_ttl}, 300, 'Default ramdisk_ttl is 300 seconds' );
ok( !exists $s_info->{record_index}, 'record_index stripped in Tier 3' );
ok( !exists $s_info->{search_block}, 'search_block stripped in Tier 3' );
ok( !exists $s_info->{match_block}, 'match_block stripped in Tier 3' );
# 3. Write data to Tier 3 volatile table
ok( $adb->insert_id( 'session_store', 101, 'token_abc', 'user_123' ), 'Record 101 inserted' );
ok( $adb->insert_id( 'session_store', 102, 'token_xyz', 'user_456' ), 'Record 102 inserted' );
# Verify physical disk has ZERO files
my $phys_db = File::Spec->catfile( $tmp, 'table', 'session_store.db' );
ok( !-e $phys_db, 'Zero physical disk files: table/session_store.db does NOT exist' );
ok( !-e File::Spec->catfile( $tmp, 'table', 'session_store.inx' ), 'Zero physical disk index files' );
# Verify RAM-disk contains the .db file
my $ram_db = File::Spec->catfile( $ramdisk_dir, 'table', 'session_store.db' );
ok( -e $ram_db, 'RAM-disk contains session_store.db' );
ok( !-e File::Spec->catfile( $ramdisk_dir, 'table', 'session_store.inx' ), 'RAM-disk has NO index files' );
# 4. Read records from Tier 3
my @rec101 = $adb->read_id( 'session_store', 101 );
is( $rec101[0], 101, 'read_id correctly returns ID 101' );
is( $rec101[1], 'token_abc', 'read_id correctly returns token_abc' );
is( $adb->table_count('session_store'), 2, 'table_count is 2' );
ok( $adb->exist_id( 'session_store', 101 ), 'exist_id is true for 101' );
ok( !$adb->exist_id( 'session_store', 999 ), 'exist_id is false for nonexistent 999' );
# 5. Modify record
ok( $adb->modify_id( 'session_store', 101, 'token_abc_renewed', 'user_123' ), 'modify_id succeeded' );
my @rec101_mod = $adb->read_id( 'session_store', 101 );
is( $rec101_mod[1], 'token_abc_renewed', 'Modified token visible in read_id' );
# 6. Delete record
ok( $adb->delete_id( 'session_store', 102 ), 'Record 102 deleted' );
ok( !$adb->exist_id( 'session_store', 102 ), 'Record 102 no longer exists' );
is( $adb->table_count('session_store'), 1, 'table_count is now 1' );
# 7. TTL Expiration Simulation
$adb->table_attr( 'temp_cache', use_ramdisk => 3, ramdisk_ttl => 5 );
ok( $adb->insert_id( 'temp_cache', 1, 'temp_val' ), 'temp_cache record inserted' );
( run in 0.670 second using v1.01-cache-2.11-cpan-4716db65a44 )