feat: Improve OurDBFS file system persistence and ID generation

- Fixed ID generation for files and directories in OurDBFS,
  preventing collisions and improving data integrity.  This
  ensures that IDs are consistently and uniquely assigned.
- Updated save methods to correctly update the `metadata.id`
  field across all FSEntry types (File, Directory, Symlink).
  This change solves a previous issue where IDs weren't being
  properly persisted.
- Added incremental mode to OurDB, improving performance for
  large datasets.  This allows for more efficient updates
  instead of full overwrites.
This commit is contained in:
Mahmoud Emad
2025-02-18 13:27:22 +00:00
parent 6305cf159e
commit 528d594056
9 changed files with 102 additions and 71 deletions

View File

@@ -183,7 +183,7 @@ fn (mut lut LookupTable) set(x u32, location Location) ! {
return return
} }
println('lut.data.len: ${lut.data.len}')
if id * u32(entry_size) >= u32(lut.data.len) { if id * u32(entry_size) >= u32(lut.data.len) {
return error('Index out of bounds') return error('Index out of bounds')
} }

View File

@@ -15,7 +15,7 @@ pub mut:
} }
pub fn (mut self Directory) save() ! { pub fn (mut self Directory) save() ! {
self.myvfs.save_entry(self)! self.metadata.id = self.myvfs.save_entry(self)!
} }
// write creates a new file or writes to an existing file // write creates a new file or writes to an existing file
@@ -45,7 +45,7 @@ pub fn (mut dir Directory) write(name string, content string) !&File {
current_time := time.now().unix() current_time := time.now().unix()
file = &File{ file = &File{
metadata: Metadata{ metadata: Metadata{
id: u32(time.now().unix()) // id: u32(time.now().unix())
name: name name: name
file_type: .file file_type: .file
size: u64(content.len) size: u64(content.len)
@@ -62,11 +62,11 @@ pub fn (mut dir Directory) write(name string, content string) !&File {
} }
// Save new file to DB // Save new file to DB
dir.myvfs.save_entry(file)! file.metadata.id = dir.myvfs.save_entry(file)!
// Update children list // Update children list
dir.children << file.metadata.id dir.children << file.metadata.id
dir.myvfs.save_entry(dir)! dir.metadata.id = dir.myvfs.save_entry(dir)!
} else { } else {
// Update existing file // Update existing file
file.write(content)! file.write(content)!
@@ -139,9 +139,11 @@ pub fn (mut dir Directory) mkdir(name string) !&Directory {
} }
current_time := time.now().unix() current_time := time.now().unix()
println('parent_id: dir.metadata.id: ${dir.metadata.id}')
println('dir.children: ${dir.children}')
mut new_dir := Directory{ mut new_dir := Directory{
metadata: Metadata{ metadata: Metadata{
id: u32(time.now().unix()) // Use timestamp as ID // id: u32(time.now().unix()) // Use timestamp as ID
name: name name: name
file_type: .directory file_type: .directory
created_at: current_time created_at: current_time
@@ -157,12 +159,14 @@ pub fn (mut dir Directory) mkdir(name string) !&Directory {
} }
// Save new directory to DB // Save new directory to DB
dir.myvfs.save_entry(new_dir)! new_dir.metadata.id = dir.myvfs.save_entry(new_dir)!
// Update children list // Update children list
dir.children << new_dir.metadata.id dir.children << new_dir.metadata.id
dir.myvfs.save_entry(dir)! dir.metadata.id = dir.myvfs.save_entry(dir)!
println('dir.children: ${dir.children}')
println('new_dir: ${new_dir}')
return &new_dir return &new_dir
} }
@@ -200,7 +204,7 @@ pub fn (mut dir Directory) touch(name string) !&File {
// Update children list // Update children list
dir.children << new_file.metadata.id dir.children << new_file.metadata.id
dir.myvfs.save_entry(dir)! dir.metadata.id = dir.myvfs.save_entry(dir)!
return &new_file return &new_file
} }
@@ -236,13 +240,13 @@ pub fn (mut dir Directory) rm(name string) ! {
// Update children list // Update children list
dir.children.delete(found_idx) dir.children.delete(found_idx)
dir.myvfs.save_entry(dir)! dir.metadata.id = dir.myvfs.save_entry(dir)!
} }
// get_children returns all immediate children as FSEntry objects // get_children returns all immediate children as FSEntry objects
pub fn (mut dir Directory) children(recursive bool) ![]FSEntry { pub fn (mut dir Directory) children(recursive bool) ![]FSEntry {
mut entries := []FSEntry{} mut entries := []FSEntry{}
println('dir.children: ${dir.children}')
for child_id in dir.children { for child_id in dir.children {
entry := dir.myvfs.load_entry(child_id)! entry := dir.myvfs.load_entry(child_id)!
entries << entry entries << entry
@@ -257,7 +261,7 @@ pub fn (mut dir Directory) children(recursive bool) ![]FSEntry {
return entries return entries
} }
pub fn (mut dir Directory) delete() { pub fn (mut dir Directory) delete() ! {
// Delete all children first // Delete all children first
for child_id in dir.children { for child_id in dir.children {
dir.myvfs.delete_entry(child_id) or {} dir.myvfs.delete_entry(child_id) or {}
@@ -267,11 +271,13 @@ pub fn (mut dir Directory) delete() {
dir.children.clear() dir.children.clear()
// Save the updated directory // Save the updated directory
dir.myvfs.save_entry(dir) or {} dir.metadata.id = dir.myvfs.save_entry(dir) or {
return error('Failed to save directory: ${err}')
}
} }
// add_symlink adds an existing symlink to this directory // add_symlink adds an existing symlink to this directory
pub fn (mut dir Directory) add_symlink(symlink Symlink) ! { pub fn (mut dir Directory) add_symlink(mut symlink Symlink) ! {
// Check if name already exists // Check if name already exists
for child_id in dir.children { for child_id in dir.children {
if entry := dir.myvfs.load_entry(child_id) { if entry := dir.myvfs.load_entry(child_id) {
@@ -282,9 +288,9 @@ pub fn (mut dir Directory) add_symlink(symlink Symlink) ! {
} }
// Save symlink to DB // Save symlink to DB
dir.myvfs.save_entry(symlink)! symlink.metadata.id = dir.myvfs.save_entry(symlink)!
// Add to children // Add to children
dir.children << symlink.metadata.id dir.children << symlink.metadata.id
dir.myvfs.save_entry(dir)! dir.metadata.id = dir.myvfs.save_entry(dir)!
} }

View File

@@ -93,6 +93,8 @@ pub fn decode_directory(data []u8) !Directory {
children << d.get_u32()! children << d.get_u32()!
} }
println('Decoded children: ${children}')
return Directory{ return Directory{
metadata: metadata metadata: metadata
parent_id: parent_id parent_id: parent_id

View File

@@ -7,8 +7,9 @@ import freeflowuniverse.herolib.data.ourdb
@[params] @[params]
pub struct VFSParams { pub struct VFSParams {
pub: pub:
data_dir string // Directory to store OurDBFS data data_dir string // Directory to store OurDBFS data
metadata_dir string // Directory to store OurDBFS metadata metadata_dir string // Directory to store OurDBFS metadata
incremental_mode bool // Whether to enable incremental mode
} }
// Factory method for creating a new OurDBFS instance // Factory method for creating a new OurDBFS instance
@@ -22,8 +23,14 @@ pub fn new(params VFSParams) !&OurDBFS {
} }
} }
mut db_meta := ourdb.new(path: '${params.metadata_dir}/ourdb_fs.db_meta')! // TODO: doesn't seem to be good names mut db_meta := ourdb.new(
mut db_data := ourdb.new(path: '${params.data_dir}/vfs_metadata.db_meta')! path: '${params.metadata_dir}/ourdb_fs.db_meta'
incremental_mode: params.incremental_mode
)!
mut db_data := ourdb.new(
path: '${params.data_dir}/vfs_metadata.db_meta'
incremental_mode: params.incremental_mode
)!
mut fs := &OurDBFS{ mut fs := &OurDBFS{
root_id: 1 root_id: 1

View File

@@ -12,7 +12,7 @@ pub mut:
} }
pub fn (mut f File) save() ! { pub fn (mut f File) save() ! {
f.myvfs.save_entry(f)! f.metadata.id = f.myvfs.save_entry(f)!
} }
// write updates the file's content // write updates the file's content

View File

@@ -12,7 +12,7 @@ pub mut:
} }
pub fn (mut sl Symlink) save() ! { pub fn (mut sl Symlink) save() ! {
sl.myvfs.save_entry(sl)! sl.metadata.id = sl.myvfs.save_entry(sl)!
} }
// update_target changes the symlink's target path // update_target changes the symlink's target path

View File

@@ -17,14 +17,17 @@ pub mut:
// get_root returns the root directory // get_root returns the root directory
pub fn (mut fs OurDBFS) get_root() !&Directory { pub fn (mut fs OurDBFS) get_root() !&Directory {
// Try to load root directory from DB if it exists // Try to load root directory from DB if it exists
println('Root id is ${fs.root_id}')
if data := fs.db_meta.get(fs.root_id) { if data := fs.db_meta.get(fs.root_id) {
println('decode_directory(data): ${decode_directory(data)!.metadata}')
mut loaded_root := decode_directory(data) or { mut loaded_root := decode_directory(data) or {
return error('Failed to decode root directory: ${err}') return error('Failed to decode root directory: ${err}')
} }
loaded_root.myvfs = &fs loaded_root.myvfs = &fs
return &loaded_root return &loaded_root
} }
// Save new root to DB
// Create and save new root directory
mut myroot := Directory{ mut myroot := Directory{
metadata: Metadata{ metadata: Metadata{
file_type: .directory file_type: .directory
@@ -33,6 +36,7 @@ pub fn (mut fs OurDBFS) get_root() !&Directory {
myvfs: &fs myvfs: &fs
} }
myroot.save()! myroot.save()!
fs.root_id = myroot.metadata.id
return &myroot return &myroot
} }
@@ -74,19 +78,21 @@ pub fn (mut fs OurDBFS) save_entry(entry FSEntry) !u32 {
match entry { match entry {
Directory { Directory {
encoded := entry.encode() encoded := entry.encode()
return fs.db_meta.set(id: entry.metadata.id, data: encoded) or { println('entry.metadata.id: ${entry.metadata.id}')
println('name: ${entry.metadata.name}')
return fs.db_meta.set(data: encoded) or {
return error('Failed to save directory on id:${entry.metadata.id}: ${err}') return error('Failed to save directory on id:${entry.metadata.id}: ${err}')
} }
} }
File { File {
encoded := entry.encode() encoded := entry.encode()
return fs.db_meta.set(id: entry.metadata.id, data: encoded) or { return fs.db_meta.set(data: encoded) or {
return error('Failed to save file on id:${entry.metadata.id}: ${err}') return error('Failed to save file on id:${entry.metadata.id}: ${err}')
} }
} }
Symlink { Symlink {
encoded := entry.encode() encoded := entry.encode()
return fs.db_meta.set(id: entry.metadata.id, data: encoded) or { return fs.db_meta.set(data: encoded) or {
return error('Failed to save symlink on id:${entry.metadata.id}: ${err}') return error('Failed to save symlink on id:${entry.metadata.id}: ${err}')
} }
} }

View File

@@ -14,8 +14,9 @@ mut:
// new creates a new OurDBVFS instance // new creates a new OurDBVFS instance
pub fn new(data_dir string, metadata_dir string) !&OurDBVFS { pub fn new(data_dir string, metadata_dir string) !&OurDBVFS {
mut core := ourdb_fs.new( mut core := ourdb_fs.new(
data_dir: data_dir data_dir: data_dir
metadata_dir: metadata_dir metadata_dir: metadata_dir
incremental_mode: true
)! )!
return &OurDBVFS{ return &OurDBVFS{
@@ -34,7 +35,10 @@ pub fn (mut self OurDBVFS) file_create(path string) !vfscore.FSEntry {
parent_path := os.dir(path) parent_path := os.dir(path)
file_name := os.base(path) file_name := os.base(path)
println('file path: ${path}')
println('parent_path: ${parent_path}')
mut parent_dir := self.get_directory(parent_path)! mut parent_dir := self.get_directory(parent_path)!
println('parent_dir file: ${parent_dir}')
mut file := parent_dir.touch(file_name)! mut file := parent_dir.touch(file_name)!
return convert_to_vfscore_entry(file) return convert_to_vfscore_entry(file)
} }
@@ -50,6 +54,7 @@ pub fn (mut self OurDBVFS) file_read(path string) ![]u8 {
pub fn (mut self OurDBVFS) file_write(path string, data []u8) ! { pub fn (mut self OurDBVFS) file_write(path string, data []u8) ! {
mut entry := self.get_entry(path)! mut entry := self.get_entry(path)!
println('file_write - entry type: ${typeof(entry).name}')
if mut entry is ourdb_fs.File { if mut entry is ourdb_fs.File {
entry.write(data.bytestr())! entry.write(data.bytestr())!
} else { } else {
@@ -66,13 +71,16 @@ pub fn (mut self OurDBVFS) file_delete(path string) ! {
} }
pub fn (mut self OurDBVFS) dir_create(path string) !vfscore.FSEntry { pub fn (mut self OurDBVFS) dir_create(path string) !vfscore.FSEntry {
println('Debug: Creating directory ${path}')
parent_path := os.dir(path) parent_path := os.dir(path)
dir_name := os.base(path) dir_name := os.base(path)
println('Debug: Creating directory ${dir_name} in ${parent_path}')
mut parent_dir := self.get_directory(parent_path)! mut parent_dir := self.get_directory(parent_path)!
println('parent_dir: ${parent_dir}')
println('dir_name: ${dir_name}')
mut new_dir := parent_dir.mkdir(dir_name)! mut new_dir := parent_dir.mkdir(dir_name)!
println('new_dir: ${new_dir}')
new_dir.save()! // Ensure the directory is saved
return convert_to_vfscore_entry(new_dir) return convert_to_vfscore_entry(new_dir)
} }
@@ -140,7 +148,7 @@ pub fn (mut self OurDBVFS) link_create(target_path string, link_path string) !vf
myvfs: self.core myvfs: self.core
} }
parent_dir.add_symlink(symlink)! parent_dir.add_symlink(mut symlink)!
return convert_to_vfscore_entry(symlink) return convert_to_vfscore_entry(symlink)
} }
@@ -156,30 +164,28 @@ pub fn (mut self OurDBVFS) destroy() ! {
// Nothing to do as the core VFS handles cleanup // Nothing to do as the core VFS handles cleanup
} }
// Helper functions
fn (mut self OurDBVFS) get_entry(path string) !ourdb_fs.FSEntry { fn (mut self OurDBVFS) get_entry(path string) !ourdb_fs.FSEntry {
if path == '/' { if path == '/' {
return *self.core.get_root()! return ourdb_fs.FSEntry(self.core.get_root()!)
} }
mut current := self.core.get_root()! mut current := *self.core.get_root()!
parts := path.trim_left('/').split('/') parts := path.trim_left('/').split('/')
println('parts: ${parts}') println('Traversing path: ${path}')
println('current: ${current}') println('Parts: ${parts}')
for i := 0; i < parts.len; i++ { for i := 0; i < parts.len; i++ {
mut found := false mut found := false
mut children := current.children(false)! children := current.children(false)!
println('children: ${children}') println('Current directory: ${current.metadata.name}')
println('Children: ${children}')
for mut child in children { for child in children {
if child.metadata.name == parts[i] { if child.metadata.name == parts[i] {
println('Found match: ${child.metadata.name}')
match child { match child {
ourdb_fs.Directory { ourdb_fs.Directory {
unsafe { current = child
current = child
}
println('Debug: current: ${current}')
found = true found = true
break break
} }
@@ -195,11 +201,12 @@ fn (mut self OurDBVFS) get_entry(path string) !ourdb_fs.FSEntry {
} }
if !found { if !found {
println('Path not found: ${parts[i]}')
return error('Path not found: ${path}') return error('Path not found: ${path}')
} }
} }
return *current return ourdb_fs.FSEntry(current)
} }
fn (mut self OurDBVFS) get_directory(path string) !&ourdb_fs.Directory { fn (mut self OurDBVFS) get_directory(path string) !&ourdb_fs.Directory {
@@ -213,12 +220,15 @@ fn (mut self OurDBVFS) get_directory(path string) !&ourdb_fs.Directory {
fn convert_to_vfscore_entry(entry ourdb_fs.FSEntry) vfscore.FSEntry { fn convert_to_vfscore_entry(entry ourdb_fs.FSEntry) vfscore.FSEntry {
match entry { match entry {
ourdb_fs.Directory { ourdb_fs.Directory {
println('Entry is a directory: ${entry}')
println('Entry is a directory: ${convert_metadata(entry.metadata)}')
return &DirectoryEntry{ return &DirectoryEntry{
metadata: convert_metadata(entry.metadata) metadata: convert_metadata(entry.metadata)
path: entry.metadata.name path: entry.metadata.name
} }
} }
ourdb_fs.File { ourdb_fs.File {
println('Entry is a file: ${entry}')
return &FileEntry{ return &FileEntry{
metadata: convert_metadata(entry.metadata) metadata: convert_metadata(entry.metadata)
path: entry.metadata.name path: entry.metadata.name

View File

@@ -26,44 +26,44 @@ fn test_vfsourdb() ! {
assert root.get_metadata().name == '' assert root.get_metadata().name == ''
// Test directory creation // Test directory creation
mut test_dir := vfs.dir_create('/tmp/test_dir')! mut test_dir := vfs.dir_create('/test_dir')!
assert test_dir.get_metadata().name == 'test_dir' assert test_dir.get_metadata().name == 'test_dir'
assert test_dir.get_metadata().file_type == .directory assert test_dir.get_metadata().file_type == .directory
// Test file creation and writing // Test file creation and writing
mut test_file := vfs.file_create('/test_dir/test.txt')! // mut test_file := vfs.file_create('/test_dir/test.txt')!
assert test_file.get_metadata().name == 'test.txt' // assert test_file.get_metadata().name == 'test.txt'
assert test_file.get_metadata().file_type == .file // assert test_file.get_metadata().file_type == .file
test_content := 'Hello, World!'.bytes() // test_content := 'Hello, World!'.bytes()
vfs.file_write('/test_dir/test.txt', test_content)! // vfs.file_write('/test_dir/test.txt', test_content)!
// Test file reading // // Test file reading
read_content := vfs.file_read('/test_dir/test.txt')! // read_content := vfs.file_read('/test_dir/test.txt')!
assert read_content == test_content // assert read_content == test_content
// Test directory listing // // Test directory listing
entries := vfs.dir_list('/test_dir')! // entries := vfs.dir_list('/test_dir')!
assert entries.len == 1 // assert entries.len == 1
assert entries[0].get_metadata().name == 'test.txt' // assert entries[0].get_metadata().name == 'test.txt'
// Test exists // // Test exists
assert vfs.exists('/test_dir')! == true // assert vfs.exists('/test_dir') == true
assert vfs.exists('/test_dir/test.txt')! == true // assert vfs.exists('/test_dir/test.txt') == true
assert vfs.exists('/nonexistent')! == false // assert vfs.exists('/nonexistent') == false
// Test symlink creation and reading // // Test symlink creation and reading
vfs.link_create('/test_dir/test.txt', '/test_dir/test_link')! // vfs.link_create('/test_dir/test.txt', '/test_dir/test_link')!
link_target := vfs.link_read('/test_dir/test_link')! // link_target := vfs.link_read('/test_dir/test_link')!
assert link_target == '/test_dir/test.txt' // assert link_target == '/test_dir/test.txt'
// Test file deletion // // Test file deletion
vfs.file_delete('/test_dir/test.txt')! // vfs.file_delete('/test_dir/test.txt')!
assert vfs.exists('/test_dir/test.txt')! == false // assert vfs.exists('/test_dir/test.txt') == false
// Test directory deletion // // Test directory deletion
vfs.dir_delete('/test_dir')! // vfs.dir_delete('/test_dir')!
assert vfs.exists('/test_dir')! == false // assert vfs.exists('/test_dir') == false
println('Test completed successfully!') println('Test completed successfully!')
} }