200.Land

leaky-bucket.ts

The TypeScript code defines a rate limiter using the leaky bucket algorithm. It has LeakyBucket and LeakyBucketTask classes. Tasks with a specified number of retries are added to the bucket and executed at intervals defined by rateMs. The bucket executes tasks one by one, retrying failed tasks until they succeed or exhaust their retries, and removes completed tasks. It allows for the addition of new tasks and stopping the task execution.

interface ILeakyBucketTaskParams {
  task: Function;
  retryCount: number;
}

interface ILeakyBucketParams {
  rateMs: number;
}

class LeakyBucketTask implements ILeakyBucketTaskParams {
  public task = this.params.task;
  public retryCount = this.params.retryCount; 
  public finished = false;

  constructor(private params: ILeakyBucketTaskParams){}

  public async run(): Promise<void> {
    try {
      await this.task();
      this.finished = true;
    } catch (e) {
      this.retryOrFinish();
    }
  }

  private retryOrFinish(): void {
    this.hasRetries() ? this.retry() : this.finished = true;
  }

  private retry(): void {
    this.retryCount--;
    this.run();
  }

  private hasRetries(): boolean {
    return this.retryCount > 0;
  }
}

class LeakyBucket {
  public bucket: LeakyBucketTask[] = [];
  private runner = setInterval(() => this.run(), this.params.rateMs);

  constructor(private params: ILeakyBucketParams){}

  public addTask(taskParams: ILeakyBucketTaskParams): void {
    this.bucket.push(new LeakyBucketTask(taskParams));
  }

  public stop(): void {
    clearInterval(this.runner);
  }

  private remove(): void {
    this.bucket.shift();
  }

  private async run(): Promise<void> {
    const task = this.getNext();
    if (!task) {
      return;
    }
    await task.run();
    if (task.finished) {
      this.remove();
    }
  }

  private getNext(): LeakyBucketTask {
    return this.bucket[0];
  }
}