Project 5: Content delivery network

UC Irvine - Fall ‘22 - ICS 45C

Read everything before you start!
Updates will be listed here:

  • added gradescope link;
    • binary file tests will be added soon, but text file test (90/100 points) are ready!
    • all tests are ready :)
  • added gradescope new line hint;
  • clarification on constructor.

Sections

Due date

This project is due at 10PM PT on Friday of week 10 (2022-12-02). You have a 15-minute buffer for any technical issues and can use your extra days if needed.

Late Submissions

If you still have late days and want to use them in this project, please do so. There is no extra credit or other bonus in “saving” late days, but also keep in mind that week 10 is the last week of classes; make sure you focus on your finals!

Context

Your fundraiser event was a blast! Many people were interested in learning more about the bank and also had a lot of fun participating in all events. Now you want to share the pictures from that day and some fliers and deals for your bank with them, but you realized that sharing files with a lot of people is very resource-intensive, and your servers might not be able to handle all the requests.

So you decided to create a content delivery network (CDN)1. In simple terms, a CDN helps websites deliver content in scale by storing recent versions that can be sent to users without asking the server. So if 1000 users want to receive a PDF of a bank offer, all users should receive a copy from the CDN, and your server might get only 1-2 requests.

The idea behind a CDN cache is that whenever the 1st user requests a file X, we ask the server for it and store it. Then, we set a freshness (e.g., 5 minutes). For any requests within 5 minutes, the cache just sends them the saved copy of the file. If a request comes after those 5 minutes, we ask the server again for the updated version. This reduces the number of requests that reach the server since we serve the file straight from the CDN.

What you have to do

You will implement a class called Cache, inside the ICS45C::CDN namespace. Your class will receive requests for text and binary files. The first time a file is requested, you should open the file and store its contents somewhere. Then, as the file is requested more times, you should return the content from memory and only read it again after it’s not fresh anymore.

To simplify both your implementation and our tests, we will use a count as the freshness level. So your class decides if it should open the file and read it again based on the number of requests it has received.

For example, let’s say we have a freshness count of 3, and the user requests file X:

  1. first time: open, read it, save its contents, then return;
  2. do NOT open the file, just return the contents you have saved in (1);
  3. do NOT open the file, just return the contents you have saved in (1);
  4. the file is not fresh anymore: open, read it, save its contents, then return;
  5. do NOT open the file, just return the contents you have saved in (4);
    and so on.

Your class must implement the following public methods:

Constructor

Cache(unsigned int freshnessCount);

A constructor that receives an unsigned int, which indicates how many times a saved file should be “re-used” before reading it from the disk again.

If freshnessCount==0 or freshnessCount==1, you should read the file every time we ask for it (i.e., it is never fresh).

Hint: you probably need a destructor to avoid memory leaks for binary files!

getText

std::string getText(std::string filepath);

This method receives the path to a text file as an argument, then you should decide if you have a fresh copy or not stored. If you already have a fresh copy stored, then you return that. If you don’t have a fresh copy, read the file from the disk and possibly update the version you have stored.

Hint: you should add a new line character (\n) after every line in the file(s) to match gradescope’s expected results.

getBinary

char* getBinary(std::string filepath);

This method receives the path to a binary file as an argument, then you should decide if you have a fresh copy or not stored. If you already have a fresh copy stored, then you return that. If you don’t have a fresh copy, read the file from the disk and possibly update the version you have stored.

isCached

bool isCached(std::string filepath);

This method returns true if the file you received matches one of your saved files. Note that the file could be either a text file or a binary file.

Tests won’t check this with a file that has just gone stale (i.e., after the last request that was fresh), so you’re free to handle that case however you want.

getFreshness

unsigned int getFreshness(std::string filepath);

This method returns how many requests are left for the given file. For example, if freshnessCount is 3, and we have requested the file only once, this method should return 2. If a file has never been requested, this method should return 0.

markFileFresh

void markFileFresh(std::string filepath);

This method receives a path to a file that might be stored in your cache. If the file is stored, you should reset its freshness to the original value. If the file is not stored, it shouldn’t do anything.

purgeCache

void purgeCache();

This method deletes all currently stored files. Any requests after this method is called should make your class read the file from the disk.

topFile

std::string topFile();

This method returns the filepath that has been requested the most. To find the top file, it doesn’t matter if it’s a binary or text file. If no file has been requested at all, you should return an empty string "".

getStats

std::string getStats();

This method returns a string report of the stats of the cache. It should follow the format below:

Cache Stats
----------
Total requests: [NUMBER OF REQUESTS]
Total files requested: [NUMBER OF DIFFERENT FILES REQUESTED]
Average requests per file: [AVERAGE REQS PER FILE]
Top file: [TOP FILE NAME] ([NUM OF REQS FOR TOP FILE] requests)
Total times read from disk: [COUNT OF DISK ACCESSES]
----------

Note that:

  • there should be a new line character after the very last ----------;
  • [SOME VALUE] indicates a value you need to calculate;
  • a request means a call to either getText or getBinary;
  • total requests/files should take into account both binary and text files;
  • for the average requests, you should use two decimal places;
  • one disk access means you had to open and read a file.

Project Hints and Tips

Class Implementation

How you implement your class and the methods we want is up to you, but containers and/or algorithms from the Standard Template Library (STL) might be helpful!

Submission

Your class should be defined in a header file named cacheCDN.h, and implemented in a source file named cacheCDN.cpp. Since all implementation decisions are up to you in this project, you can get somewhat empty starter files here: cacheCDN.h and cacheCDN.cpp

You should submit your code (both header and source files!) on gradescope: https://www.gradescope.com/courses/443728/assignments/2460415/
You can submit it as often as you want; just remember that gradescope might not give you instant feedback, and it can take a little bit to compile and run your code.

Your code will be checked for compilation (e.g., warnings), style, static problems (e.g., memory leaks), and functionality issues. Compilation, style, and static checks will be completely open, so you know what needs to be changed. Functionality tests will be a mix of open and hidden ones. You should be able to debug your code based on the open ones.

For this project, all functionality tests use unit tests. That means we’re checking if your functions manipulate the structures the way we want and that your returns are valid. You should not have a main function in your submission.

Testing your code

You can download these files to test your submission:

p5-test-main.cpp includes cacheCDN.h and calls all methods using the sample text and binary files. It’s at most a few calls per method, so gradescope will do a more thorough check of your submission, but this is a quick way to test things and a good start. If you haven’t implemented everything yet, you can comment out whatever parts that you have not done.