Cloud Storage System, Part 3: Users and Capacity
Reported by candidates from Ramp's online assessment. Pattern, common pitfall, and the honest play if you blank under the timer.
Ramp's May 2026 OA is a multi-level in-memory cloud storage build, and this is Part 3: users and capacity. It's a design problem, not an algorithm puzzle. You're extending a file map from the earlier levels with owners, per-user capacity limits and merging. The adapter feeds operation rows left to right against one shared state, so every bug compounds. If the earlier levels are shaky, this one collapses. StealthCoder sits invisibly on your screen as a safety net if you blank mid-assessment, but you should walk in knowing the data layout.
The problem
Your task is to implement a simple cloud storage system. All operations that should be supported are listed below. Solving this task consists of several levels. Subsequent levels are opened when the current level is correctly solved. You always have access to the data for the current and all previous levels. Requirements Your task is to implement a simple cloud storage system that maps objects (files) to their metainformation. Specifically, the storage should maintain files and information about them, including each file name and size. This system is in-memory; it does not use the real filesystem. The levels are cumulative: Level 1 supports adding, retrieving, and deleting files. Level 2 displays the largest files matching a prefix. Level 3 adds capacity-limited users and user merging. Level 4 backs up and restores a user's files. To move to the next level, all tests at the current level must pass. Note The queries never call operations that produce a collision between a file name and a directory name. FastPrep Operation-Sequence Adapter FastPrep calls the function once with operations. Process the rows from left to right while preserving one shared storage state. Every row starts with an uppercase operation name followed by that method's string arguments. Return one string-array row for every input operation: Encode a Boolean as ["true"] or ["false"]. Encode an integer as one element, such as ["10"]. Encode None as an empty row []. Return the formatted list from GET_N_LARGEST directly; an empty list is also []. Multipart Series Part 1: File Operations Part 2: Largest Files Part 3: Users and Capacity Part 4: Backup and Restore Level 1: File Operations The cloud storage system should support file manipulation. add_file(self, name: str, size: int) -> bool adds a new file name to the storage. size is the amount of memory required in bytes. The operation fails if a file with the same name already exists. Return True if the file was added successfully or False otherwise. The adapter row is ["ADD_FILE", name, size]. Files added this way are owned by the unlimited admin user. get_file_size(self, name: str) -> int | None returns the size of file name if it exists, or None otherwise. The adapter row is ["GET_FILE_SIZE", name]. delete_file(self, name: str) -> int | None deletes file name. Return the deleted file size when deletion succeeds, or None if the file does not exist. The adapter row is ["DELETE_FILE", name]. Level 2: Largest Files Implement an operation for retrieving statistics about files with a specific prefix. get_n_largest(self, prefix: str, n: int) -> list[str] returns the names of the top n largest files whose names start with prefix, formatted as ["<name_1>(<size_1>)",..., "<name_n>(<size_n>)"]. Sort matching files by size in descending order. Break a size tie by file name in lexicographical ascending order. If there are no matching files, return an empty list. If fewer than n files match, return all of them in the specified format. The adapter row is ["GET_N_LARGEST", prefix, n]. The visible source table contains one malformed no-match call whose second argument is a file name even though the declared signature requires n: int. The judged contract follows the declared signature: n is an integer, and a prefix with no matches returns an empty row. Level 3: Users and Capacity Support queries from different users. All users share one common filesystem, and every non-admin user has a storage-capacity limit. add_user(self, user_id: str, capacity: int) -> bool adds a new user with capacity bytes. The total size of files owned by user_id cannot exceed this limit. The operation fails if the user already exists. Return True on success and False otherwise. The adapter row is ["ADD_USER", user_id, capacity]. add_file_by(self, user_id: str, name: str, size: int) -> int | None behaves like add_file, but the new file is owned by user_id. The operation fails when the user is absent, the name is already occupied, or the addition would exceed the user's capacity. Return the user's remaining capacity after success, or None otherwise. The adapter row is ["ADD_FILE_BY", user_id, name, size]. Every ADD_FILE operation from Level 1 is run by the admin user, who has unlimited storage capacity. merge_user(self, user_id_1: str, user_id_2: str) -> int | None merges user_id_2 into user_id_1. Transfer ownership of every file owned by user_id_2, add the remaining storage capacity of user_id_2 to user_id_1's limit, and delete user_id_2. Return user_id_1's remaining capacity, or None when either user is absent or the IDs are equal. Neither merge argument is admin. The adapter row is ["MERGE_USER", user_id_1, user_id_2]. Function cloudStorageLevel3(operations: String[][]) → String[][] Examples Example 1 operations = [["ADD_FILE","/dir1/dir2/file.txt","10"],["ADD_FILE","/dir1/dir2/file.txt","5"],["GET_FILE_SIZE","/dir1/dir2/file.txt"],["DELETE_FILE","/non-existing.file"],["DELETE_FILE","/dir1/dir2/file.txt"],["GET_FILE_SIZE","/not-existing.file"]] return = [["true"],["false"],["10"],[],["10"],[]] The first add succeeds, the duplicate add fails, and the existing file size is 10. Deleting a missing file returns None; deleting the existing file returns 10; the final lookup again returns None. Example 2 operations = [["ADD_FILE","/dir/file1.txt","5"],["ADD_FILE","/dir/file2","20"],["ADD_FILE","/dir/deeper/file3.mov","9"],["GET_N_LARGEST","/dir","2"],["GET_N_LARGEST","/dir/file","3"],["GET_N_LARGEST","/another_dir","3"],["ADD_FILE","/big_file.mp4","20"],["GET_N_LARGEST","/","2"]] return = [["true"],["true"],["true"],["/dir/file2(20)","/dir/deeper/file3.mov(9)"],["/dir/file2(20)","/dir/file1.txt(5)"],[],["true"],["/big_file.mp4(20)","/dir/file2(20)"]] Prefix filtering keeps matching names only. Size is the primary order; equal sizes are ordered lexicographically. The no-match query returns an empty row. Example 3 operations = [["ADD_USER","user1","100"],["ADD_USER","user2","40"],["ADD_FILE_BY","user1","/a","60"],["ADD_FILE_BY","user2","/b","10"],["ADD_FILE_BY","user2","/c","30"],["MERGE_USER","user1","user2"],["GET_FILE_SIZE","/b"],["ADD_FILE_BY","user2","/d","1"],["ADD_FILE_BY","user1","/d","40"],["MERGE_USER","user1","user1"]] return = [["true"],["true"],["40"],["30"],["0"],["40"],["10"],[],["0"],[]] The merge transfers both files and the remaining capacity, then deletes user2. Calls for the deleted user and a self-merge return None. Constraints Every operation row is well formed and uses an operation available at this level. Every numeric argument is a base-10 integer string whose value and all arithmetic results fit in a signed 64-bit integer. File names, prefixes, and user IDs are non-empty case-sensitive strings. The input never creates a collision between a file name and a directory name. All users share one global file-name namespace. The admin user exists initially and has unlimited capacity. Process operations in the supplied order. The storage starts empty.
Reported by candidates. Source: FastPrep
Pattern and pitfall
The trick is bookkeeping. Keep one dict from file name to (size, owner), and one dict from user to (capacity, used). Every add, delete and merge updates both in the same code path, so used never drifts. add_file_by must check three things before mutating: user exists, name is free, and used + size fits capacity. Admin from add_file has no limit, so don't give it a fake capacity, just skip the check. The pitfall is deleting a file and forgetting to subtract from its owner's used total. Another is Level 2 getting slow: sorting all matches per query is fine for modest input, but don't re-scan on every call if the data is large. Keep the output formatting exact: Booleans as true/false strings, None as an empty row. If the merge rules trip you up mid-OA, StealthCoder can read the spec and hand you a working version.
If this hits your live OA and you blank, StealthCoder solves it in seconds, invisible to the proctor.
You can drill Cloud Storage System, Part 3: Users and Capacity cold, or you can hedge it. StealthCoder runs invisibly during screen share and surfaces a working solution in under 2 seconds. The proctor sees the IDE. They don't see what's behind it. Built by an Amazon engineer who would have shipped this the night before his JPMorgan OA if he'd had it.
Get StealthCoderRelated leaked OAs
You've seen the question.
Make sure you actually pass Ramp's OA.
Ramp reuses patterns across OAs. Built by an Amazon engineer who would have shipped this the night before his JPMorgan OA if he'd had it. Works on HackerRank, CodeSignal, CoderPad, and Karat.
Cloud Storage System, Part 3: Users and Capacity FAQ
How hard is Ramp's Cloud Storage System Part 3 really?+
Medium on difficulty, high on carefulness. There's no clever algorithm. The risk is state consistency across add, delete and user operations, plus exact output formatting. Candidates usually lose points to small invariant bugs, not to missing a technique.
What's the core data structure trick?+
Store files as name to (size, owner), and users as id to (capacity, used). Update both together on every mutation. Treat admin as unlimited by skipping the capacity check instead of inventing a huge number.
Do I need to redo Levels 1 and 2 first?+
Yes, in effect. Levels are cumulative and all earlier tests must pass. Part 3 builds on add_file, delete_file and get_n_largest, so any bug there fails you here. Make sure deletion also credits the owner's used bytes.
What output format does the adapter expect?+
One string-array row per operation. Booleans become ["true"] or ["false"], integers become a single string element, None becomes an empty row, and lists from get_n_largest return directly. Empty lists are also [].
How do I prepare in 48 hours?+
Write the Level 1 to 3 class from scratch twice. Test sequences where a user fills capacity, deletes a file, then adds again. Check duplicate names, missing users and a failed add leaving state unchanged. That covers most of what breaks.