adjusted redit session manager to use bottleneck

pull/2/head
j 2024-03-23 01:02:22 -04:00
parent 5f6d0671cf
commit de4da43242
1 changed files with 53 additions and 4 deletions

View File

@ -1,18 +1,33 @@
import axios, { AxiosInstance, AxiosError } from 'axios'; import axios, { AxiosInstance, AxiosError, AxiosResponse, AxiosRequestConfig } from 'axios';
const qs = require('qs'); const qs = require('qs');
import dotenv from 'dotenv'; import dotenv from 'dotenv';
import axiosRetry from 'axios-retry'; import axiosRetry from 'axios-retry';
import axiosThrottle from 'axios-request-throttle'; import Bottleneck from 'bottleneck';
import { DatabaseService } from '../../db/services/Database'; import { DatabaseService } from '../../db/services/Database';
dotenv.config(); dotenv.config();
class RedditSessionManager { class RedditSessionManager {
private static instance: RedditSessionManager; private static instance: RedditSessionManager;
public axiosInstance: AxiosInstance; public readonly axiosInstance: AxiosInstance;
private limiter: Bottleneck;
private constructor() { private constructor() {
axiosThrottle.use(axios, { requestsPerSecond: 1 }); // Throttle setup
// Initialize the Bottleneck limiter
this.limiter = new Bottleneck({
id: "reddit-limiter",
datastore: "ioredis",
clearDatastore: false,
clientOptions: {
host: process.env.REDIS_HOST,
port: Number(process.env.REDIS_PORT),
password: process.env.REDIS_PASSWORD || undefined, // Use undefined if no password is set
enableOfflineQueue: true
},
maxConcurrent: 1, // Maximum number of concurrent requests
minTime: 1000 // Minimum time between dispatches of requests in milliseconds
});
this.axiosInstance = axios.create({ this.axiosInstance = axios.create({
baseURL: 'https://oauth.reddit.com/', // Base URL for OAuth2 Reddit API baseURL: 'https://oauth.reddit.com/', // Base URL for OAuth2 Reddit API
@ -20,6 +35,10 @@ class RedditSessionManager {
'User-Agent': 'CrossTalk PM/0.1 by Whitneywisconson' 'User-Agent': 'CrossTalk PM/0.1 by Whitneywisconson'
} }
}); });
// Wrap axios requests with the limiter
this.wrapAxiosInstance(this.axiosInstance);
axiosRetry(this.axiosInstance, { axiosRetry(this.axiosInstance, {
retries: 3, retries: 3,
retryDelay: this.retryDelayStrategy, retryDelay: this.retryDelayStrategy,
@ -35,6 +54,10 @@ class RedditSessionManager {
return RedditSessionManager.instance; return RedditSessionManager.instance;
} }
public async shutdown(): Promise<void> {
await this.limiter.disconnect();
}
private async initializeAuthentication() { private async initializeAuthentication() {
// Check the database for an existing token // Check the database for an existing token
const currentToken = await DatabaseService.getCurrentOAuthToken(this.axiosInstance.defaults.baseURL as string); const currentToken = await DatabaseService.getCurrentOAuthToken(this.axiosInstance.defaults.baseURL as string);
@ -103,6 +126,32 @@ class RedditSessionManager {
const status = error.response?.status ?? 0; const status = error.response?.status ?? 0;
return status === 429 || status >= 400; return status === 429 || status >= 400;
} }
private wrapAxiosInstance(instance: AxiosInstance): void {
// Wrap the get method
const originalGet = instance.get;
instance.get = <T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R> => {
return this.limiter.schedule(() => originalGet.apply(instance, [url, config])) as Promise<R>;
};
// Wrap the post method
const originalPost = instance.post;
instance.post = <T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R> => {
return this.limiter.schedule(() => originalPost.apply(instance, [url, data, config])) as Promise<R>;
};
// Wrap the put method
const originalPut = instance.put;
instance.put = <T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R> => {
return this.limiter.schedule(() => originalPut.apply(instance, [url, data, config])) as Promise<R>;
};
// Wrap the delete method
const originalDelete = instance.delete;
instance.delete = <T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R> => {
return this.limiter.schedule(() => originalDelete.apply(instance, [url, config])) as Promise<R>;
};
}
} }
export default RedditSessionManager; export default RedditSessionManager;