The directory package provides a tool for managing related subspaces in FoundationDB through hierarchical, path-based organization. Directories are a recommended approach for administering applications, offering a level of indirection for accessing subspaces through a filesystem-like interface.
Package Path: github.com/apple/foundationdb/bindings/go/src/fdb/directory
Key Features:
Use Case: Each application should create or open at least one directory to manage its subspaces, providing logical organization separate from physical key layout.
For general guidance, see the Directories section of the Developer Guide.
Directories are identified by hierarchical paths represented as slices of strings:
[]string{"app", "users", "profiles"} represents a nested structureThe directory layer provides indirection between logical paths and physical prefixes:
[]string{"myapp", "data"})Directories support optional layer metadata:
{ .api }
type Directory interface {
CreateOrOpen(t fdb.Transactor, path []string, layer []byte) (DirectorySubspace, error)
Open(rt fdb.ReadTransactor, path []string, layer []byte) (DirectorySubspace, error)
Create(t fdb.Transactor, path []string, layer []byte) (DirectorySubspace, error)
CreatePrefix(t fdb.Transactor, path []string, layer []byte, prefix []byte) (DirectorySubspace, error)
Move(t fdb.Transactor, oldPath []string, newPath []string) (DirectorySubspace, error)
MoveTo(t fdb.Transactor, newAbsolutePath []string) (DirectorySubspace, error)
Remove(t fdb.Transactor, path []string) (bool, error)
Exists(rt fdb.ReadTransactor, path []string) (bool, error)
List(rt fdb.ReadTransactor, path []string) ([]string, error)
GetLayer() []byte
GetPath() []string
}Directory represents a subspace of keys in a FoundationDB database, identified by a hierarchical path. All path parameters are relative to the Directory on which the method is called.
{ .api }
func (d Directory) CreateOrOpen(t fdb.Transactor, path []string, layer []byte) (DirectorySubspace, error)Opens the directory at the specified path, creating it if it doesn't exist. Parent directories are created automatically if necessary.
Parameters:
t: Transactor for the operationpath: Path relative to this Directorylayer: Optional layer metadata (nil for none)Returns:
Behavior:
Example:
// Create or open a directory for user data
dir := directory.Root()
userDir, err := dir.CreateOrOpen(db, []string{"myapp", "users"}, nil)
if err != nil {
log.Fatal(err)
}func (d Directory) Open(rt fdb.ReadTransactor, path []string, layer []byte) (DirectorySubspace, error)Opens an existing directory at the specified path.
Parameters:
rt: ReadTransactor for the operationpath: Path relative to this Directorylayer: Optional layer metadata to verify (nil for none)Returns:
ErrDirNotExists if directory doesn't existErrParentDirDoesNotExist if parent directory missingExample:
// Open an existing directory
dir := directory.Root()
userDir, err := dir.Open(db, []string{"myapp", "users"}, nil)
if err == directory.ErrDirNotExists {
// Handle directory not found
}func (d Directory) Create(t fdb.Transactor, path []string, layer []byte) (DirectorySubspace, error)Creates a new directory at the specified path.
Parameters:
t: Transactor for the operationpath: Path relative to this Directorylayer: Optional layer metadata to store (nil for none)Returns:
ErrDirAlreadyExists if directory already existsBehavior:
Example:
// Create a new directory with layer metadata
dir := directory.Root()
layer := []byte("user-profiles")
profileDir, err := dir.Create(db, []string{"myapp", "profiles"}, layer)
if err == directory.ErrDirAlreadyExists {
// Handle already exists
}func (d Directory) CreatePrefix(t fdb.Transactor, path []string, layer []byte, prefix []byte) (DirectorySubspace, error)Creates a directory with a manually specified prefix instead of automatic allocation.
Parameters:
t: Transactor for the operationpath: Path relative to this Directorylayer: Optional layer metadataprefix: Byte slice prefix to use for this directory's subspaceReturns:
Restrictions:
allowManualPrefixes=trueExample:
// Create a custom root directory that allows manual prefixes
nodeSS := subspace.FromBytes([]byte{0xFE})
contentSS := subspace.AllKeys()
customRoot := directory.NewDirectoryLayer(nodeSS, contentSS, true)
// Now can use manual prefixes
prefix := []byte{0x01, 0x02}
dir, err := customRoot.CreatePrefix(db, []string{"custom"}, nil, prefix)func (d Directory) Move(t fdb.Transactor, oldPath []string, newPath []string) (DirectorySubspace, error)Moves a directory from one path to another, both relative to this Directory.
Parameters:
t: Transactor for the operationoldPath: Current path of directorynewPath: Target path for directoryReturns:
Important Notes:
Example:
// Move a directory to reorganize
dir := directory.Root()
movedDir, err := dir.Move(db,
[]string{"myapp", "old-location"},
[]string{"myapp", "new-location"})func (d Directory) MoveTo(t fdb.Transactor, newAbsolutePath []string) (DirectorySubspace, error)Moves this directory to a new absolute path (relative to the root directory).
Parameters:
t: Transactor for the operationnewAbsolutePath: New absolute path relative to rootReturns:
Important Notes:
Example:
// Move a directory to a new location
userDir, _ := directory.Open(db, []string{"myapp", "users"}, nil)
movedDir, err := userDir.MoveTo(db, []string{"myapp", "v2", "users"})func (d Directory) Remove(t fdb.Transactor, path []string) (bool, error)Removes the directory at the specified path, including all its content and subdirectories.
Parameters:
t: Transactor for the operationpath: Path of directory to removeReturns:
true if directory existed and was removedfalse if no directory exists at pathWarning: Clients that have already opened this directory can still insert data into its contents after removal. Use appropriate coordination.
Example:
// Remove a directory and all its contents
dir := directory.Root()
removed, err := dir.Remove(db, []string{"myapp", "temp-data"})
if removed {
fmt.Println("Directory removed successfully")
}func (d Directory) Exists(rt fdb.ReadTransactor, path []string) (bool, error)Checks if a directory exists at the specified path.
Parameters:
rt: ReadTransactor for the operationpath: Path relative to this DirectoryReturns:
true if directory existsfalse if directory doesn't existExample:
// Check if a directory exists before opening
dir := directory.Root()
exists, err := dir.Exists(db, []string{"myapp", "cache"})
if !exists {
// Create the directory
dir.Create(db, []string{"myapp", "cache"}, nil)
}func (d Directory) List(rt fdb.ReadTransactor, path []string) ([]string, error)Returns the names of immediate subdirectories at the specified path.
Parameters:
rt: ReadTransactor for the operationpath: Path of directory to listReturns:
ErrDirNotExists if directory doesn't existExample:
// List all subdirectories
dir := directory.Root()
subdirs, err := dir.List(db, []string{"myapp"})
// subdirs might be: ["users", "products", "orders"]func (d Directory) GetLayer() []byteReturns the layer metadata specified when this directory was created.
Returns: Layer byte slice, or empty slice if no layer was specified
Example:
dir, _ := directory.Open(db, []string{"myapp", "users"}, nil)
layer := dir.GetLayer()
if string(layer) == "user-profiles" {
// Handle user profile directory
}func (d Directory) GetPath() []stringReturns the path with which this directory was opened.
Returns: Path slice representing the directory's location
Example:
dir, _ := directory.Open(db, []string{"myapp", "users", "profiles"}, nil)
path := dir.GetPath()
// path is: []string{"myapp", "users", "profiles"}{ .api }
type DirectorySubspace interface {
subspace.Subspace
Directory
}DirectorySubspace combines the Directory and Subspace interfaces, representing a Directory that can also be used as a Subspace to store key/value pairs.
Key Characteristics:
Example Usage:
// Open a directory and use it as both Directory and Subspace
userDir, _ := directory.CreateOrOpen(db, []string{"myapp", "users"}, nil)
// Use as a Subspace to store data
db.Transact(func(tr fdb.Transaction) (interface{}, error) {
// Pack a key using the directory's prefix
key := userDir.Pack(tuple.Tuple{"user123", "email"})
tr.Set(key, []byte("user@example.com"))
return nil, nil
})
// Use as a Directory to create subdirectories
profileDir, _ := userDir.CreateOrOpen(db, []string{"profiles"}, nil){ .api }
func Root() DirectoryReturns the default root directory for the database.
Returns: Default root Directory
Characteristics:
0xFE0x00 through 0xFDCaution: May conflict with existing data in databases with other partitioning schemes. Consider using NewDirectoryLayer for non-empty databases.
Example:
// Use the default root directory
root := directory.Root()
appDir, err := root.CreateOrOpen(db, []string{"myapp"}, nil)func NewDirectoryLayer(nodeSS, contentSS subspace.Subspace, allowManualPrefixes bool) DirectoryCreates a custom root directory with specified subspaces for metadata and content.
Parameters:
nodeSS: Subspace for storing directory metadatacontentSS: Subspace for allocating directory content prefixesallowManualPrefixes: If true, allows CreatePrefix; if false, only automatic allocationReturns: New root Directory with custom configuration
Use Cases:
Example:
// Create a custom directory layer in a restricted keyspace
nodeSS := subspace.FromBytes([]byte{0xAA, 0xFE})
contentSS := subspace.FromBytes([]byte{0xAA})
customRoot := directory.NewDirectoryLayer(nodeSS, contentSS, false)
// Use the custom root
appDir, err := customRoot.CreateOrOpen(db, []string{"myapp"}, nil)Comparison with Default Root:
// Default root configuration (equivalent to):
defaultRoot := directory.NewDirectoryLayer(
subspace.FromBytes([]byte{0xFE}),
subspace.AllKeys(),
false,
)These functions operate on the default root directory returned by Root(). They provide a convenient alternative to explicitly calling Root().
{ .api }
func CreateOrOpen(t fdb.Transactor, path []string, layer []byte) (DirectorySubspace, error)Opens or creates a directory at the path relative to the default root directory.
Parameters:
t: Transactor for the operationpath: Path relative to default rootlayer: Optional layer metadataReturns:
Equivalent to: directory.Root().CreateOrOpen(t, path, layer)
Example:
// Create or open using package-level function
userDir, err := directory.CreateOrOpen(db, []string{"myapp", "users"}, nil)
// Equivalent to:
root := directory.Root()
userDir, err := root.CreateOrOpen(db, []string{"myapp", "users"}, nil)func Open(rt fdb.ReadTransactor, path []string, layer []byte) (DirectorySubspace, error)Opens an existing directory at the path relative to the default root directory.
Parameters:
rt: ReadTransactor for the operationpath: Path relative to default rootlayer: Optional layer metadata to verifyReturns:
ErrDirNotExists if directory doesn't existEquivalent to: directory.Root().Open(rt, path, layer)
Example:
// Open using package-level function
userDir, err := directory.Open(db, []string{"myapp", "users"}, nil)func Create(t fdb.Transactor, path []string, layer []byte) (DirectorySubspace, error)Creates a new directory at the path relative to the default root directory.
Parameters:
t: Transactor for the operationpath: Path relative to default rootlayer: Optional layer metadataReturns:
ErrDirAlreadyExists if directory existsEquivalent to: directory.Root().Create(t, path, layer)
Example:
// Create using package-level function
profileDir, err := directory.Create(db, []string{"myapp", "profiles"}, []byte("profiles"))func Move(t fdb.Transactor, oldPath []string, newPath []string) (DirectorySubspace, error)Moves a directory from oldPath to newPath, both relative to the default root directory.
Parameters:
t: Transactor for the operationoldPath: Current path of directorynewPath: Target path for directoryReturns:
Equivalent to: directory.Root().Move(t, oldPath, newPath)
Example:
// Move using package-level function
movedDir, err := directory.Move(db,
[]string{"myapp", "old"},
[]string{"myapp", "new"})func Exists(rt fdb.ReadTransactor, path []string) (bool, error)Checks if a directory exists at the path relative to the default root directory.
Parameters:
rt: ReadTransactor for the operationpath: Path relative to default rootReturns:
true if directory exists, false otherwiseEquivalent to: directory.Root().Exists(rt, path)
Example:
// Check existence using package-level function
exists, err := directory.Exists(db, []string{"myapp", "cache"})func List(rt fdb.ReadTransactor, path []string) ([]string, error)Lists immediate subdirectories at the path relative to the default root directory.
Parameters:
rt: ReadTransactor for the operationpath: Path to listReturns:
ErrDirNotExists if directory doesn't existEquivalent to: directory.Root().List(rt, path)
Example:
// List using package-level function
subdirs, err := directory.List(db, []string{"myapp"}){ .api }
var (
// ErrDirAlreadyExists is returned when trying to create a directory that already exists
ErrDirAlreadyExists = errors.New("the directory already exists")
// ErrDirNotExists is returned when opening or listing a directory that does not exist
ErrDirNotExists = errors.New("the directory does not exist")
// ErrParentDirDoesNotExist is returned when opening a directory and one or more
// parent directories in the path do not exist
ErrParentDirDoesNotExist = errors.New("the parent directory does not exist")
)Usage:
dir, err := directory.Open(db, []string{"myapp", "users"}, nil)
if err == directory.ErrDirNotExists {
// Handle directory not found - maybe create it
dir, err = directory.Create(db, []string{"myapp", "users"}, nil)
} else if err == directory.ErrParentDirDoesNotExist {
// Parent directory missing - create parent first
directory.CreateOrOpen(db, []string{"myapp"}, nil)
dir, err = directory.Create(db, []string{"myapp", "users"}, nil)
}
newDir, err := directory.Create(db, []string{"myapp", "cache"}, nil)
if err == directory.ErrDirAlreadyExists {
// Directory already exists - open it instead
newDir, _ = directory.Open(db, []string{"myapp", "cache"}, nil)
}package main
import (
"log"
"github.com/apple/foundationdb/bindings/go/src/fdb"
"github.com/apple/foundationdb/bindings/go/src/fdb/directory"
"github.com/apple/foundationdb/bindings/go/src/fdb/tuple"
)
func main() {
fdb.MustAPIVersion(730)
db := fdb.MustOpenDefault()
defer db.Close()
// Create or open a directory for your application
appDir, err := directory.CreateOrOpen(db, []string{"myapp"}, nil)
if err != nil {
log.Fatal(err)
}
// Create subdirectories for different data types
userDir, err := appDir.CreateOrOpen(db, []string{"users"}, nil)
if err != nil {
log.Fatal(err)
}
productDir, err := appDir.CreateOrOpen(db, []string{"products"}, nil)
if err != nil {
log.Fatal(err)
}
// Use directories as subspaces to store data
db.Transact(func(tr fdb.Transaction) (interface{}, error) {
// Store user data
userKey := userDir.Pack(tuple.Tuple{"user123", "email"})
tr.Set(userKey, []byte("user@example.com"))
// Store product data
productKey := productDir.Pack(tuple.Tuple{"product456", "price"})
tr.Set(productKey, []byte("29.99"))
return nil, nil
})
}// Create a deep directory hierarchy
func setupApplicationDirectories(db fdb.Database) error {
// Main application directory
appDir, err := directory.CreateOrOpen(db, []string{"myapp"}, nil)
if err != nil {
return err
}
// User data hierarchy
userDir, err := appDir.CreateOrOpen(db, []string{"users"}, nil)
if err != nil {
return err
}
// User subdirectories
_, err = userDir.CreateOrOpen(db, []string{"profiles"}, []byte("user-profiles"))
if err != nil {
return err
}
_, err = userDir.CreateOrOpen(db, []string{"settings"}, []byte("user-settings"))
if err != nil {
return err
}
_, err = userDir.CreateOrOpen(db, []string{"activity"}, []byte("user-activity"))
if err != nil {
return err
}
// Product data hierarchy
productDir, err := appDir.CreateOrOpen(db, []string{"products"}, nil)
if err != nil {
return err
}
_, err = productDir.CreateOrOpen(db, []string{"catalog"}, nil)
if err != nil {
return err
}
_, err = productDir.CreateOrOpen(db, []string{"inventory"}, nil)
if err != nil {
return err
}
return nil
}// Define layer constants for different data types
const (
LayerUserProfiles = "user-profiles-v1"
LayerUserSettings = "user-settings-v1"
LayerProductCatalog = "product-catalog-v2"
)
func openTypedDirectory(db fdb.Database, path []string, expectedLayer string) (directory.DirectorySubspace, error) {
// Open directory and verify layer matches expected type
dir, err := directory.Open(db, path, []byte(expectedLayer))
if err != nil {
return nil, fmt.Errorf("failed to open directory with layer %s: %w", expectedLayer, err)
}
return dir, nil
}
func createVersionedDirectory(db fdb.Database, path []string, layer string) (directory.DirectorySubspace, error) {
// Create directory with layer metadata for versioning
dir, err := directory.Create(db, path, []byte(layer))
if err == directory.ErrDirAlreadyExists {
// Directory exists - verify layer matches
dir, err = directory.Open(db, path, []byte(layer))
if err != nil {
return nil, fmt.Errorf("directory exists but layer mismatch: %w", err)
}
} else if err != nil {
return nil, err
}
return dir, nil
}
// Usage
func main() {
fdb.MustAPIVersion(730)
db := fdb.MustOpenDefault()
defer db.Close()
// Create directory with layer metadata
profileDir, err := createVersionedDirectory(db,
[]string{"myapp", "users", "profiles"},
LayerUserProfiles)
if err != nil {
log.Fatal(err)
}
// Open and verify layer
verifiedDir, err := openTypedDirectory(db,
[]string{"myapp", "users", "profiles"},
LayerUserProfiles)
if err != nil {
log.Fatal(err)
}
// Use the directory
log.Printf("Opened directory with layer: %s", verifiedDir.GetLayer())
}// Reorganize directory structure
func reorganizeDirectories(db fdb.Database) error {
root := directory.Root()
// Check if old location exists
exists, err := root.Exists(db, []string{"myapp", "old-location"})
if err != nil {
return err
}
if exists {
// Move directory to new location
movedDir, err := root.Move(db,
[]string{"myapp", "old-location"},
[]string{"myapp", "v2", "new-location"})
if err != nil {
return fmt.Errorf("failed to move directory: %w", err)
}
log.Printf("Moved directory to: %v", movedDir.GetPath())
}
return nil
}
// Move directory using MoveTo
func moveToNewLocation(db fdb.Database, dir directory.DirectorySubspace) error {
currentPath := dir.GetPath()
newPath := append([]string{"archive"}, currentPath...)
movedDir, err := dir.MoveTo(db, newPath)
if err != nil {
return fmt.Errorf("failed to move directory: %w", err)
}
log.Printf("Moved from %v to %v", currentPath, movedDir.GetPath())
return nil
}// Recursively list entire directory tree
func listDirectoryTree(db fdb.Database, dir directory.Directory, path []string, indent string) error {
// List subdirectories
subdirs, err := dir.List(db, path)
if err != nil {
return err
}
for _, name := range subdirs {
fullPath := append(path, name)
fmt.Printf("%s%s/\n", indent, name)
// Recursively list subdirectories
if err := listDirectoryTree(db, dir, fullPath, indent+" "); err != nil {
return err
}
}
return nil
}
// Usage
func main() {
fdb.MustAPIVersion(730)
db := fdb.MustOpenDefault()
defer db.Close()
root := directory.Root()
fmt.Println("Directory structure:")
if err := listDirectoryTree(db, root, []string{"myapp"}, " "); err != nil {
log.Fatal(err)
}
// Output might look like:
// Directory structure:
// users/
// profiles/
// settings/
// activity/
// products/
// catalog/
// inventory/
}// Demonstrate using DirectorySubspace for both organization and storage
func userDataExample(db fdb.Database) error {
// Create user directory
userDir, err := directory.CreateOrOpen(db, []string{"myapp", "users"}, nil)
if err != nil {
return err
}
// Use as Subspace to store user data
_, err = db.Transact(func(tr fdb.Transaction) (interface{}, error) {
// Store user records using tuple keys
userID := "user123"
// Email
emailKey := userDir.Pack(tuple.Tuple{userID, "email"})
tr.Set(emailKey, []byte("user@example.com"))
// Name
nameKey := userDir.Pack(tuple.Tuple{userID, "name"})
tr.Set(nameKey, []byte("John Doe"))
// Age
ageKey := userDir.Pack(tuple.Tuple{userID, "age"})
tr.Set(ageKey, []byte("30"))
return nil, nil
})
if err != nil {
return err
}
// Read user data back
_, err = db.Transact(func(tr fdb.Transaction) (interface{}, error) {
userID := "user123"
// Read email
emailKey := userDir.Pack(tuple.Tuple{userID, "email"})
email := tr.Get(emailKey).MustGet()
fmt.Printf("User email: %s\n", string(email))
return nil, nil
})
return err
}
// Scan all data within a directory
func scanDirectoryData(db fdb.Database, dirPath []string) error {
dir, err := directory.Open(db, dirPath, nil)
if err != nil {
return err
}
_, err = db.Transact(func(tr fdb.Transaction) (interface{}, error) {
// Get range covering entire directory subspace
kr, err := fdb.PrefixRange(dir.Bytes())
if err != nil {
return nil, err
}
// Iterate over all keys in the directory
iter := tr.GetRange(kr, fdb.RangeOptions{}).Iterator()
for iter.Advance() {
kv, err := iter.Get()
if err != nil {
return nil, err
}
// Unpack the key to get tuple components
t, err := dir.Unpack(kv.Key)
if err != nil {
return nil, err
}
fmt.Printf("Key: %v, Value: %s\n", t, string(kv.Value))
}
return nil, nil
})
return err
}// Create isolated directory layers for different applications
func setupPartitionedApplications(db fdb.Database) error {
// Application A - uses keyspace 0xAA
appA_nodeSS := subspace.FromBytes([]byte{0xAA, 0xFE})
appA_contentSS := subspace.FromBytes([]byte{0xAA})
appA_root := directory.NewDirectoryLayer(appA_nodeSS, appA_contentSS, false)
appA_dir, err := appA_root.CreateOrOpen(db, []string{"users"}, nil)
if err != nil {
return err
}
// Application B - uses keyspace 0xBB
appB_nodeSS := subspace.FromBytes([]byte{0xBB, 0xFE})
appB_contentSS := subspace.FromBytes([]byte{0xBB})
appB_root := directory.NewDirectoryLayer(appB_nodeSS, appB_contentSS, false)
appB_dir, err := appB_root.CreateOrOpen(db, []string{"products"}, nil)
if err != nil {
return err
}
// Both applications can use same logical paths without conflict
log.Printf("App A users directory: %v", appA_dir.GetPath())
log.Printf("App B products directory: %v", appB_dir.GetPath())
return nil
}
// Create a directory layer that allows manual prefixes
func setupCustomPrefixes(db fdb.Database) error {
// Create root that allows manual prefixes
nodeSS := subspace.FromBytes([]byte{0xCC, 0xFE})
contentSS := subspace.FromBytes([]byte{0xCC})
customRoot := directory.NewDirectoryLayer(nodeSS, contentSS, true)
// Now we can use CreatePrefix
prefix := []byte{0xCC, 0x01}
dir, err := customRoot.CreatePrefix(db,
[]string{"fixed-prefix-dir"},
nil,
prefix)
if err != nil {
return err
}
log.Printf("Created directory with manual prefix: %x", dir.Bytes())
return nil
}// Safely create directory only if it doesn't exist
func createIfNotExists(db fdb.Database, path []string, layer []byte) (directory.DirectorySubspace, error) {
// Check if directory exists
exists, err := directory.Exists(db, path)
if err != nil {
return nil, fmt.Errorf("failed to check existence: %w", err)
}
if exists {
// Open existing directory
log.Printf("Directory %v already exists, opening...", path)
return directory.Open(db, path, layer)
}
// Create new directory
log.Printf("Creating new directory %v", path)
return directory.Create(db, path, layer)
}
// Handle all directory errors gracefully
func openDirectoryWithFallback(db fdb.Database, path []string, layer []byte) (directory.DirectorySubspace, error) {
dir, err := directory.Open(db, path, layer)
if err == directory.ErrDirNotExists {
// Directory doesn't exist - create it
log.Printf("Directory %v not found, creating...", path)
return directory.Create(db, path, layer)
} else if err == directory.ErrParentDirDoesNotExist {
// Parent missing - create parent first
log.Printf("Parent directory missing, creating hierarchy...")
return directory.CreateOrOpen(db, path, layer)
}
return dir, err
}// Remove directory with confirmation
func removeDirectorySafely(db fdb.Database, path []string) error {
// Check if directory exists
exists, err := directory.Exists(db, path)
if err != nil {
return err
}
if !exists {
log.Printf("Directory %v does not exist", path)
return nil
}
// List contents to show what will be removed
subdirs, err := directory.List(db, path)
if err != nil {
return err
}
if len(subdirs) > 0 {
log.Printf("Warning: Directory %v has subdirectories: %v", path, subdirs)
}
// Remove the directory
removed, err := directory.Remove(db, path)
if err != nil {
return fmt.Errorf("failed to remove directory: %w", err)
}
if removed {
log.Printf("Successfully removed directory %v", path)
}
return nil
}
// Clean up temporary directories
func cleanupTempDirectories(db fdb.Database) error {
root := directory.Root()
// List all subdirectories under temp
tempDirs, err := root.List(db, []string{"myapp", "temp"})
if err == directory.ErrDirNotExists {
// No temp directory, nothing to clean
return nil
} else if err != nil {
return err
}
// Remove each temporary subdirectory
for _, name := range tempDirs {
path := []string{"myapp", "temp", name}
_, err := root.Remove(db, path)
if err != nil {
log.Printf("Failed to remove %v: %v", path, err)
} else {
log.Printf("Removed temporary directory: %s", name)
}
}
return nil
}Use descriptive path names: Choose clear, meaningful names for directory paths
// Good
directory.CreateOrOpen(db, []string{"myapp", "users", "profiles"}, nil)
// Avoid
directory.CreateOrOpen(db, []string{"ma", "u", "p"}, nil)Organize by domain: Group related data hierarchically
// User domain
[]string{"myapp", "users", "profiles"}
[]string{"myapp", "users", "settings"}
[]string{"myapp", "users", "activity"}
// Product domain
[]string{"myapp", "products", "catalog"}
[]string{"myapp", "products", "inventory"}Keep hierarchy shallow: Avoid overly deep directory structures (3-5 levels typically sufficient)
Use layers for type safety: Specify layer metadata to prevent incompatible directory access
layer := []byte("user-profiles-v1")
directory.Create(db, path, layer)Version your layers: Include version information in layer names for schema evolution
const (
LayerV1 = "user-profiles-v1"
LayerV2 = "user-profiles-v2"
)Consistent layer naming: Use consistent naming conventions across your application
Check specific errors: Handle directory-specific errors appropriately
if err == directory.ErrDirNotExists {
// Create missing directory
} else if err == directory.ErrDirAlreadyExists {
// Open existing directory
}Use CreateOrOpen for flexibility: When either creating or opening is acceptable
dir, err := directory.CreateOrOpen(db, path, layer)Cache DirectorySubspace references: Reuse directory references rather than repeatedly opening
// Open once and reuse
userDir, _ := directory.Open(db, []string{"users"}, nil)
// Use multiple times
for _, userID := range userIDs {
key := userDir.Pack(tuple.Tuple{userID})
// ... use key
}Batch directory operations: Create multiple directories in single transaction when possible
db.Transact(func(tr fdb.Transaction) (interface{}, error) {
dir1, _ := root.Create(tr, []string{"dir1"}, nil)
dir2, _ := root.Create(tr, []string{"dir2"}, nil)
return nil, nil
})