There are two main components of a filer: directories and files. My previous approach was to use some sequance number to generate directoryId. However, this is not scalable. The id generation itself is a bottleneck. It needs careful locking and deduplication checking to get a directoryId. In a second design, each directory is deterministically mapped to UUID version 3, which uses MD5 to map a tuple of to a version 3 UUID. However, this UUID3 approach is logically the same as storing the full path. Storing the full path is the simplest design. separator is a special byte, 0x00. When writing a file: => fildId, file properties For folders: The filer breaks the directory path into folders. for each folder: if it is not in cache: check whether the folder is created in the KVS, if not: set => directory properties if no permission for the folder: break The filer caches the most recently used folder permissions with a TTL. So any folder permission change needs to wait TTL interval to take effect. When listing the directory: prefix scan of using (the folder full path + separator) as the prefix The downside: 1. Rename a folder will need to recursively process all sub folders and files. 2. Move a folder will need to recursively process all sub folders and files. So these operations are not allowed if the folder is not empty. Allowing: 1. Rename a file 2. Move a file to a different folder 3. Delete an empty folder