CrushedPixel

Untitled

Feb 10th, 2017
151
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import * as AtomicTimer from "../../../utils/AtomicTimer";
  2. import { floorToMinutes } from "../../../utils/TimeUtils";
  3. import { RedisService } from "../RedisService";
  4. import * as winston from "winston";
  5.  
  6. // TODO: unit test the hell outta this
  7. export class ApiRequestService extends RedisService {
  8.  
  9.     // Every application may make up to 5000 requests per hour.
  10.     // Request counts are stored in one-minute blocks which get added
  11.     // and removed from a list.
  12.  
  13.     // expires after 1 hour
  14.     static readonly expiry: number = 60;
  15.  
  16.     async incrApiRequest(application: string) {
  17.         // first, increment the temporary and total counter
  18.         let temp: number = Number(await this.client.incr("app:${application}:request:tempCounter"));
  19.         await this.client.incr("app:${application}:request:totalCounter");
  20.  
  21.         await this.update(application, temp);
  22.     }
  23.  
  24.     async getApiRequests(application: string): Promise<number> {
  25.         let total: number = await this.update(application);
  26.         if (!total) total = Number(await this.client.get("app:${application}:request:totalCounter"));
  27.  
  28.         return total;
  29.     }
  30.  
  31.     async update(application: string, temp?: number): Promise<number> {
  32.         // the amount of minutes passed since the unix epoch
  33.         const now: number = floorToMinutes((await AtomicTimer.syncTime()).unix());
  34.  
  35.         // the last time the list was updated
  36.         const last: number = Number(await this.client.get("app:${application}:request:lastUpdate"));
  37.  
  38.         const minutesPassed: number = last == 0 ? 0 : now - last;
  39.  
  40.         // retrieve the temp counter unless provided
  41.         if (!temp) temp = Number(await this.client.get("app:${application}:request:tempCounter"));
  42.         let total: number;
  43.  
  44.         // if the list has less entries than it should, fill it up
  45.         const llen: number = Number(await this.client.llen("app:${application}:request:list"));
  46.         if (llen < ApiRequestService.expiry) {
  47.             if (llen != 0) winston.warn("Less than %s elements in in request count list for application %s", llen, application);
  48.             for (let i = llen; i < ApiRequestService.expiry; i++) {
  49.                 await this.client.rpush("app:${application}:request:list", 0);
  50.             }
  51.         }
  52.  
  53.         for (let i = 0; i < minutesPassed; i++) {
  54.             // remove the last element of the list and subtract it from the counter
  55.             let c: number = Number(await this.client.lpop("app:${application}:request:list"));
  56.  
  57.             total = Number(await this.client.decrby("app:${application}:request:totalCounter", c));
  58.  
  59.             // push the temp counter onto the list and reset it
  60.             await this.client.rpush("app:${application}:request:list", temp);
  61.             await this.client.set("app:${application}:request:tempCounter", 0);
  62.             temp = 0;
  63.         }
  64.  
  65.         // update the last update time
  66.         await this.client.set("app:${application}:request:lastUpdate", now);
  67.  
  68.         return total;
  69.     }
  70.  
  71. }
Advertisement
Add Comment
Please, Sign In to add comment