Back

Cloud Storage System (Online Assessment)

Low-Level DesignCodingSoftware EngineerReported Feb, 2026

Overview

Your task is to implement a simple cloud storage system that maps objects (files) to their metainformation. Specifically, the storage system should maintain files along with some information about them (name, size, etc.).

Note that this system should be in-memory, you do not need to work with the real filesystem.

Plan your design according to the level specifications below:

Level 1: The cloud storage system should support adding new files, retrieving, and copying files

Level 2: The cloud storage system should support finding files by matching prefixes and suffixes

Level 3: The cloud storage system should support adding users with various capacity limits

Level 4: The cloud storage system should support compressing and decompressing files

To move to the next level, you need to pass all the tests at this level.

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.

Input/Output Format

Input: You will receive a list of queries (array of string arrays) to the system.

Output: Return an array of strings representing the returned values of all queries.

Note:

Each query will only call one operation

It is guaranteed that the given queries will never call operations that result in collisions between file and directory names

Level 1: Basic Operations

Description

The cloud storage system should support operations to add files, copy files, and get files stored on the system.

Operations

ADD_FILE

ADD_FILE <name> <size>

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

Returns "true" if the file was added successfully, or "false" otherwise

COPY_FILE

COPY_FILE <nameFrom> <nameTo>

Copies the file at nameFrom to nameTo

The operation fails if:

nameFrom points to a file that does not exist

nameFrom points to a directory

The specified file already exists at nameTo

Returns "true" if the file was copied successfully, or "false" otherwise

GET_FILE_SIZE

GET_FILE_SIZE <name>

Returns a string representing the size of the file name if it exists

Returns an empty string "" if the file does not exist

Example

Queries:

[

  ["ADD_FILE", "/dir1/dir2/file.txt", "10"],
  ["COPY_FILE", "/root-existing_file.txt", "/dir1/file.txt"],
  ["COPY_FILE", "/dir1/dir2/file.txt", "/dir1/file.txt"],
  ["ADD_FILE", "/dir1/file.txt", "15"],
  ["COPY_FILE", "/dir1/file.txt", "/dir1/dir2/file.txt"],
  ["GET_FILE_SIZE", "/dir1/file.txt"],
  ["GET_FILE_SIZE", "/not-existing.file"]
]

Execution:

Output: ["true", "false", "true", "false", "false", "10", ""]

Level 2: Finding Files by Prefix and Suffix

Description

Implement support for retrieving file names by searching directories via prefixes and suffixes.

New Operations

FIND_FILE

FIND_FILE <prefix> <suffix>

Searches for files with names starting with prefix and ending with suffix

Returns a string representing all matching files in this format:

"<name1>(<size1>), <name2>(<size2>), ..."

Output should be sorted in descending order of file sizes

In case of ties (same file size), sort lexicographically by file name

If no files match the required properties, returns an empty string ""

Example

Queries:

[

  ["ADD_FILE", "/root/dir/another_dir/file.mp3", "10"],
  ["ADD_FILE", "/root/file.mp3", "5"],
  ["ADD_FILE", "/root/music/file.mp3", "7"],
  ["COPY_FILE", "/root/music/file.mp3", "/root/dir/file.mp3"],
  ["FIND_FILE", "/root", ".mp3"],
  ["FIND_FILE", "/root", "file.txt"],
  ["FIND_FILE", "/dir", "file.mp3"]
]

Execution:

Output: ["true", "true", "true", "true", "/root/dir/another_dir/file.mp3(10), /root/dir/file.mp3(7), /root/music/file.mp3(7), /root/file.mp3(5)", "", ""]

Level 3: User Capacity Limits

Description

Implement support for different users sending queries to the system. All users share a common filesystem in the cloud storage, but each user is assigned an individual storage capacity limit.

New Operations

ADD_USER

ADD_USER <userId> <capacity>

Adds a new user to the system with userId and capacity as their storage limit in bytes

The total size of all files owned by userId cannot exceed capacity

The operation fails if a user with userId already exists

Returns "true" if a user is successfully created, or "false" otherwise

ADD_FILE_BY

ADD_FILE_BY <userId> <name> <size>

Behaves the same way as the ADD_FILE operation from Level 1

The added file should be owned by the user with userId

A new file cannot be added to the storage if doing so will exceed the user's capacity limit

Returns a string representing the remaining storage capacity for userId if the file is successfully added

Returns an empty string "" otherwise

Note: All queries calling the ADD_FILE operation implemented during Level 1 are run by the user with userId = "admin", who has unlimited storage capacity. Also assume that the COPY_FILE operation preserves the ownership of the original file, and copying a file owned by a non-admin user is allowed only if that user has enough remaining capacity for the copy.

UPDATE_CAPACITY

UPDATE_CAPACITY <userId> <capacity>

Changes the maximum storage capacity for the user with userId

If the total size of all user's files exceeds the new capacity, the largest files (sorted lexicographically in case of a tie) should be removed from the storage until the total size of all remaining files no longer exceeds the new capacity

Returns a string representing the number of removed files

Returns an empty string "" if a user with userId does not exist

Example

Queries:

[

  ["ADD_USER", "user1", "125"],
  ["ADD_USER", "user1", "100"],
  ["ADD_USER", "user2", "100"],
  ["ADD_FILE_BY", "user1", "/dir/file.big", "50"],
  ["ADD_FILE_BY", "user1", "/file.med", "30"],
  ["ADD_FILE_BY", "user2", "/file.med", "40"],
  ["COPY_FILE", "/file.med", "/dir/another/file.med"],
  ["COPY_FILE", "/file.med", "/dir/another/another/file.med"],
  ["ADD_FILE_BY", "user1", "/dir/file.small", "10"],
  ["ADD_FILE", "/dir/admin_file", "200"],
  ["ADD_FILE_BY", "user1", "/dir/file.small", "5"],
  ["ADD_FILE_BY", "user1", "/my_folder/file.huge", "100"],
  ["ADD_FILE_BY", "user3", "/my_folder/file.huge", "100"],
  ["UPDATE_CAPACITY", "user1", "300"],
  ["UPDATE_CAPACITY", "user1", "50"],
  ["UPDATE_CAPACITY", "user2", "1000"]
]

Execution:

Output: ["true", "false", "true", "75", "45", "", "true", "false", "5", "true", "", "", "", "0", "2", "0"]

Level 4: File Compression

Description

Implement support for file compression.

New Operations

COMPRESS_FILE

COMPRESS_FILE <userId> <name>

Compresses the file name if it belongs to userId

The compressed file is replaced with a new file named <name>.COMPRESSED

The size of the newly created file should be equal to half of the original file

The size of all files is guaranteed to be even, so there should be no fractional calculations

It is also guaranteed that name never ends with .COMPRESSED (for this operation)

Compressed files cannot be compressed again - i.e., it never ends with .COMPRESSED

Compressed files cannot be added via ADD_FILE (because file names can only contain lowercase letters)

Returns a string representing the remaining storage capacity for userId if the file was compressed successfully

Returns an empty string "" otherwise

Note: Because file names can only contain lowercase letters, compressed files cannot be added via ADD_FILE.

It is guaranteed that all COPY_FILE operations will preserve the suffix .COMPRESSED.

DECOMPRESS_FILE

DECOMPRESS_FILE <userId> <name>

Reverts the compression of the file name if it belongs to userId

It is guaranteed that name always ends with .COMPRESSED for this operation

If decompression results in the userId exceeding their storage capacity limit OR a decompressed version of the file with the given name already exists, the operation fails

Returns a string representing the remaining capacity of userId if the file was decompressed successfully

Returns an empty string "" otherwise

Example

Queries:

[

  ["ADD_USER", "user1", "1000"],
  ["ADD_USER", "user2", "5000"],
  ["ADD_FILE_BY", "user1", "/dir/file.mp4", "500"],
  ["COMPRESS_FILE", "user2", "/dir/file.mp4"],
  ["COMPRESS_FILE", "user3", "/dir/file.mp4"],
  ["COMPRESS_FILE", "user1", "/folder/non_existing_file"],
  ["COMPRESS_FILE", "user1", "/dir/file.mp4"],
  ["GET_FILE_SIZE", "/dir/file.mp4.COMPRESSED"],
  ["GET_FILE_SIZE", "/dir/file.mp4"],
  ["COPY_FILE", "/dir/file.mp4.COMPRESSED", "/file.mp4.COMPRESSED"],
  ["ADD_FILE_BY", "user1", "/dir/file.mp4", "500"],
  ["DECOMPRESS_FILE", "user1", "/dir/file.mp4.COMPRESSED"],
  ["UPDATE_CAPACITY", "user1", "2000"],
  ["DECOMPRESS_FILE", "user2", "/dir/file.mp4.COMPRESSED"],
  ["DECOMPRESS_FILE", "user3", "/dir/file.mp4.COMPRESSED"],
  ["DECOMPRESS_FILE", "user1", "/dir/file.mp4.COMPRESSED"],
  ["DECOMPRESS_FILE", "user1", "/file.mp4.COMPRESSED"]
]

Execution:

Output: ["true", "true", "500", "", "", "", "750", "250", "", "true", "0", "", "0", "", "", "", "750"]


Reference solution

#12 Cloud Storage System (Online Assessment) — Solution

✦ AI-Generated Solution · Coding (OA) · Comprehensive Full level-by-level reference implementation. Verified: this code reproduces every official example output in the problem statement (all 4 levels pass).


How to approach this OA

These CodeSignal-style multi-level OAs reward a clean, extensible data model over clever tricks. Pick state that survives all four levels up front so each new level is an additive method, not a rewrite:

  • A single files dict: name -> {size, owner}. (Directories are implicit — only files are stored, and the problem guarantees no name collisions.)
  • A users dict: userId -> {cap, used}, seeded with admin at infinite capacity so Level-1 ADD_FILE is just ADD_FILE_BY("admin", ...).

Everything else (prefix/suffix search, capacity eviction, compression) reads or mutates those two structures.

Reference Implementation (Python)

class CloudStorage:
    def __init__(self):
        self.files = {}                                   # name -> {"size", "owner"}
        self.users = {"admin": {"cap": float("inf"), "used": 0}}

    # ---------- Level 1: add / copy / size ----------
    def add_file(self, name, size):
        return self._add(  "admin", name, int(size), level1=True)

    def _add(self, user, name, size, level1=False):
        if name in self.files:
            return "false" if level1 else ""
        u = self.users.get(user)
        if u is None or u["used"] + size > u["cap"]:
            return "false" if level1 else ""
        self.files[name] = {"size": size, "owner": user}
        u["used"] += size
        return "true" if level1 else str(int(u["cap"] - u["used"]))

    def copy_file(self, src, dst):
        if src not in self.files or dst in self.files:
            return "false"
        f = self.files[src]; u = self.users[f["owner"]]
        if u["used"] + f["size"] > u["cap"]:              # copy keeps original owner
            return "false"
        self.files[dst] = {"size": f["size"], "owner": f["owner"]}
        u["used"] += f["size"]
        return "true"

    def get_file_size(self, name):
        return str(self.files[name]["size"]) if name in self.files else ""

    # ---------- Level 2: prefix + suffix search ----------
    def find_file(self, prefix, suffix):
        hits = [(n, f["size"]) for n, f in self.files.items()
                if n.startswith(prefix) and n.endswith(suffix)]
        hits.sort(key=lambda x: (-x[1], x[0]))            # size desc, then name asc
        return ", ".join(f"{n}({s})" for n, s in hits)

    # ---------- Level 3: users + capacity ----------
    def add_user(self, user, cap):
        if user in self.users:
            return "false"
        self.users[user] = {"cap": int(cap), "used": 0}
        return "true"

    def add_file_by(self, user, name, size):
        return self._add(user, name, int(size))

    def update_capacity(self, user, cap):
        u = self.users.get(user)
        if u is None:
            return ""
        u["cap"] = cap = int(cap)
        if u["used"] <= cap:
            return "0"
        owned = [(n, f["size"]) for n, f in self.files.items() if f["owner"] == user]
        owned.sort(key=lambda x: (-x[1], x[0]))           # largest first; tie -> name asc
        removed = 0
        for n, s in owned:
            if u["used"] <= cap:
                break
            del self.files[n]; u["used"] -= s; removed += 1
        return str(removed)

    # ---------- Level 4: compress / decompress ----------
    def compress_file(self, user, name):
        if name not in self.files or self.files[name]["owner"] != user:
            return ""
        comp = name + ".COMPRESSED"
        if comp in self.files:
            return ""
        f = self.files.pop(name); u = self.users[user]
        u["used"] -= f["size"]
        half = f["size"] // 2                             # sizes guaranteed even
        self.files[comp] = {"size": half, "owner": user}
        u["used"] += half
        return str(int(u["cap"] - u["used"]))

    def decompress_file(self, user, name):
        if name not in self.files or self.files[name]["owner"] != user:
            return ""
        orig = name[:-len(".COMPRESSED")]
        if orig in self.files:                            # decompressed twin already exists
            return ""
        f = self.files[name]; u = self.users[user]; full = f["size"] * 2
        if u["used"] - f["size"] + full > u["cap"]:       # would exceed capacity
            return ""
        del self.files[name]; u["used"] -= f["size"]
        self.files[orig] = {"size": full, "owner": user}
        u["used"] += full
        return str(int(u["cap"] - u["used"]))

The traps that fail hidden tests

  1. Copy preserves the original owner and consumes that owner's capacity — not the caller's. A non-admin copy fails if the owner lacks remaining capacity.
  2. UPDATE_CAPACITY eviction order: remove largest files first; break ties by lexicographically smaller name. Return the count removed, "" only if the user doesn't exist (returning "0" when nothing is removed is correct).
  3. Capacity bookkeeping must be incremental (used field), not recomputed by scanning files each call — recomputation is correct but O(N) per query and risks TLE at Level 3/4 volumes.
  4. Decompress capacity check compares against the size delta (full - half), and also fails if the decompressed name already exists.
  5. ADD_FILE_BY returns remaining capacity as a string, while Level-1 ADD_FILE returns "true"/"false". Same internal path, different return contract — keep them separate.

Complexity

  • add/copy/get/compress/decompress: O(1) (dict ops).
  • find_file: O(N log N) in the number of stored files (scan + sort of matches).
  • update_capacity: O(N log N) to sort the user's owned files; eviction itself is O(k).

Verification

Running the four official example query sequences from the prompt through CloudStorage reproduces every expected output exactly:

Level 1: PASS   -> ["true","false","true","false","false","10",""]
Level 2: PASS   -> [...,"/root/dir/another_dir/file.mp3(10), /root/dir/file.mp3(7), /root/music/file.mp3(7), /root/file.mp3(5)","",""]
Level 3: PASS   -> ["true","false","true","75","45","","true","false","5","true","","","","0","2","0"]
Level 4: PASS   -> ["true","true","500","","","","750","250","","true","0","","0","","","","750"]
Auto-save enabled
Loading editor…
Output
Run your code to see the output here.