dimanche 12 juillet 2020

reporting when user exceeded the limit for button clicks

I got a function which which was mentioned in on of my previous questions: Function to calculate how often users are pressing a button

void onNewButtonPress(int64_t nanoseconds_timestamp, int32_t user_id);

This function will be called each time a user with a user_id is clicking the button. Where nanoseconds_timestamp parameter is the time in nanoseconds since the epoch.

My task is to do the rate limit checks for each user. Every user has its own click rate limit. Rate limit for every user can be retrieved using the following function:

const struct rate_limit* getLimit(uint32_t userId);

getlimit will return a pointer to a struct Rate_limit

struct rate_limit
{
    uint32_t max;
    uint32_t milliseconds;
};

Where max is the maximum number of clicks a user can make within the interval of milliseconds value. For example if max = 400 and milliseconds = 200, then a user can only make 400 clicks within 200 ms interval.

When a user breaches the limit the function should report it using report function with a prototype as follows.

void report(uint32_t user_id);

How would you, folks, detect when user breaches its limit. Below is my solution with comments. I still beleive that there may be much smarter and better solution and would like to hear your opinions.

My implementation details are as follows

I created a structure which will contain information about every user history.

struct UserTrackingInfo
{
    /* Limit which is returned when a user clicks for the first time */
    const rate_limit* limit;

    /* Variable which will get incremented each time a user is making a click */
    uint32_t breachCount{0};

    /* Timestamp in nanoseconds of when breachPeriod started. Whenever a user clicks for the first time
     * this timestamp will be initialized
     * Time will be sliced in breach periods whose duration is equal to the max of the rate_limit */
    uint64_t breachPeriodStartTs{0};
};

I created a map where key is the user_id and value is UserTrackingInfo

std::map<int32_t, struct UserTrackingInfo > tInfo;

This is my proposed implementation of the function onNewButtonPress.

void onNewButtonPress(uint64_t& nanoseconds_timestamp, int32_t user_id)
{
    auto &tref = tInfo[user_id];


    if (tref.breachPeriodStartTs == 0){
        /* if a user hasnt clicked before, get the limit for the user and initialize a breach period start time stamp */
        tref.limit = getLimit(user_id);
        tref.breachPeriodStartTs = nanoseconds_timestamp;
    }
    else
    {
        /* Increment how many times used clicked a button */
        tref.breachCount++;

        /* Get number of ns passed since the time when last period started */
        int64_t passed = nanoseconds_timestamp - tref.breachPeriodStartTs;

        /* If we reached a limit, report it */
        if (passed < (tref.limit->milliseconds * 1000)){
            if (tref.breachCount > tref.limit->max){
                report(user_id);
            }
            /* we dont start a new period yet */
        }
        else{
            /* If the limit hasnt been reached yet */
            /* User may have pressed after the end of the breach period. Or he didnt make any clicks for the duration of a couple of breach periods */

            /* Find number of breach measure periods that may have been missed */
            uint64_t num_periods = passed / (tref.limit->milliseconds * 1000);

            /* Get duration of the passed periods in nanoseconds */
            uint64_t num_periods_ns = num_periods * (tref.limit->milliseconds * 1000);

            /* Set the the start time of the current breach measure period */
            /* and reset breachCount */
            tref.breachPeriodStartTs = tref.breachPeriodStartTs + num_periods_ns;
            tref.breachCount = 1;
        }
    }
}

Aucun commentaire:

Enregistrer un commentaire