@mulingai-npm/redis 3.29.4 → 3.29.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,5 +3,6 @@ export declare enum RedisDatabase {
3
3
  MULINGSTREAM_CHUNK = 1,
4
4
  MULINGSTREAM_SPEAKER = 2,
5
5
  CONTENT_DATA = 3,
6
- ACCOUNT_DATA = 4
6
+ ACCOUNT_DATA = 4,
7
+ USAGE_DATA = 5
7
8
  }
@@ -8,4 +8,5 @@ var RedisDatabase;
8
8
  RedisDatabase[RedisDatabase["MULINGSTREAM_SPEAKER"] = 2] = "MULINGSTREAM_SPEAKER";
9
9
  RedisDatabase[RedisDatabase["CONTENT_DATA"] = 3] = "CONTENT_DATA";
10
10
  RedisDatabase[RedisDatabase["ACCOUNT_DATA"] = 4] = "ACCOUNT_DATA";
11
+ RedisDatabase[RedisDatabase["USAGE_DATA"] = 5] = "USAGE_DATA";
11
12
  })(RedisDatabase = exports.RedisDatabase || (exports.RedisDatabase = {}));
@@ -0,0 +1,65 @@
1
+ import { RedisClient } from '../redis-client';
2
+ /**
3
+ * Pending usage data for a user
4
+ */
5
+ export interface PendingUsageData {
6
+ userId: string;
7
+ billingMs: number;
8
+ roomDurations: Record<string, number>;
9
+ lastUpdated: number;
10
+ }
11
+ /**
12
+ * Usage Data Manager
13
+ *
14
+ * Stores pending streaming usage in Redis for batch DB writes.
15
+ * Pipeline service writes here every 5 seconds during streaming.
16
+ * Job service flushes to DB every 30-60 seconds.
17
+ *
18
+ * Key structure:
19
+ * - usage:pending:{userId} - Hash with billing and room usage
20
+ * - usage:pending:users - Set of all user IDs with pending usage
21
+ */
22
+ export declare class UsageDataManager {
23
+ private redisClient;
24
+ private readonly EXPIRATION;
25
+ constructor(redisClient: RedisClient);
26
+ /**
27
+ * Key for a user's pending usage data
28
+ */
29
+ private userKey;
30
+ /**
31
+ * Key for the set of users with pending usage
32
+ */
33
+ private usersSetKey;
34
+ /**
35
+ * Add streaming usage for a user
36
+ * Called by pipeline-service every 5 seconds during streaming
37
+ */
38
+ addUsage(userId: string, roomId: string, durationMs: number): Promise<void>;
39
+ /**
40
+ * Get all users with pending usage
41
+ */
42
+ getUsersWithPendingUsage(): Promise<string[]>;
43
+ /**
44
+ * Get pending usage for a specific user
45
+ */
46
+ getPendingUsage(userId: string): Promise<PendingUsageData | null>;
47
+ /**
48
+ * Get all pending usage data (for batch flush)
49
+ */
50
+ getAllPendingUsage(): Promise<PendingUsageData[]>;
51
+ /**
52
+ * Clear pending usage for a specific user (after successful DB write)
53
+ */
54
+ clearUserUsage(userId: string): Promise<void>;
55
+ /**
56
+ * Clear all pending usage (after successful batch flush)
57
+ */
58
+ clearAllUsage(): Promise<void>;
59
+ /**
60
+ * Flush a single user's usage to DB immediately
61
+ * Called when session closes to ensure no data is lost
62
+ * Returns the usage data that was flushed (for logging)
63
+ */
64
+ getAndClearUserUsage(userId: string): Promise<PendingUsageData | null>;
65
+ }
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UsageDataManager = void 0;
4
+ /**
5
+ * Usage Data Manager
6
+ *
7
+ * Stores pending streaming usage in Redis for batch DB writes.
8
+ * Pipeline service writes here every 5 seconds during streaming.
9
+ * Job service flushes to DB every 30-60 seconds.
10
+ *
11
+ * Key structure:
12
+ * - usage:pending:{userId} - Hash with billing and room usage
13
+ * - usage:pending:users - Set of all user IDs with pending usage
14
+ */
15
+ class UsageDataManager {
16
+ constructor(redisClient) {
17
+ this.EXPIRATION = 24 * 60 * 60; // 24 hours TTL
18
+ this.redisClient = redisClient;
19
+ }
20
+ /**
21
+ * Key for a user's pending usage data
22
+ */
23
+ userKey(userId) {
24
+ return `usage:pending:${userId}`;
25
+ }
26
+ /**
27
+ * Key for the set of users with pending usage
28
+ */
29
+ usersSetKey() {
30
+ return 'usage:pending:users';
31
+ }
32
+ /**
33
+ * Add streaming usage for a user
34
+ * Called by pipeline-service every 5 seconds during streaming
35
+ */
36
+ async addUsage(userId, roomId, durationMs) {
37
+ const key = this.userKey(userId);
38
+ // Increment billing total
39
+ await this.redisClient.hincrby(key, 'billingMs', durationMs);
40
+ // Increment room-specific duration
41
+ await this.redisClient.hincrby(key, `room:${roomId}`, durationMs);
42
+ // Update timestamp
43
+ await this.redisClient.hset(key, { lastUpdated: Date.now().toString() });
44
+ // Set expiration
45
+ await this.redisClient.expire(key, this.EXPIRATION);
46
+ // Add user to the set of users with pending usage
47
+ await this.redisClient.sadd(this.usersSetKey(), userId);
48
+ await this.redisClient.expire(this.usersSetKey(), this.EXPIRATION);
49
+ }
50
+ /**
51
+ * Get all users with pending usage
52
+ */
53
+ async getUsersWithPendingUsage() {
54
+ const members = await this.redisClient.smembers(this.usersSetKey());
55
+ return members || [];
56
+ }
57
+ /**
58
+ * Get pending usage for a specific user
59
+ */
60
+ async getPendingUsage(userId) {
61
+ const key = this.userKey(userId);
62
+ const data = await this.redisClient.hgetall(key);
63
+ if (!data || Object.keys(data).length === 0) {
64
+ return null;
65
+ }
66
+ const billingMs = parseInt(data.billingMs || '0', 10);
67
+ const lastUpdated = parseInt(data.lastUpdated || '0', 10);
68
+ // Extract room durations (keys starting with "room:")
69
+ const roomDurations = {};
70
+ for (const [field, value] of Object.entries(data)) {
71
+ if (field.startsWith('room:')) {
72
+ const roomId = field.substring(5); // Remove "room:" prefix
73
+ roomDurations[roomId] = parseInt(value, 10);
74
+ }
75
+ }
76
+ return {
77
+ userId,
78
+ billingMs,
79
+ roomDurations,
80
+ lastUpdated
81
+ };
82
+ }
83
+ /**
84
+ * Get all pending usage data (for batch flush)
85
+ */
86
+ async getAllPendingUsage() {
87
+ const userIds = await this.getUsersWithPendingUsage();
88
+ const results = [];
89
+ for (const userId of userIds) {
90
+ const usage = await this.getPendingUsage(userId);
91
+ if (usage && usage.billingMs > 0) {
92
+ results.push(usage);
93
+ }
94
+ }
95
+ return results;
96
+ }
97
+ /**
98
+ * Clear pending usage for a specific user (after successful DB write)
99
+ */
100
+ async clearUserUsage(userId) {
101
+ const key = this.userKey(userId);
102
+ await this.redisClient.del(key);
103
+ await this.redisClient.srem(this.usersSetKey(), userId);
104
+ }
105
+ /**
106
+ * Clear all pending usage (after successful batch flush)
107
+ */
108
+ async clearAllUsage() {
109
+ const userIds = await this.getUsersWithPendingUsage();
110
+ for (const userId of userIds) {
111
+ await this.redisClient.del(this.userKey(userId));
112
+ }
113
+ await this.redisClient.del(this.usersSetKey());
114
+ }
115
+ /**
116
+ * Flush a single user's usage to DB immediately
117
+ * Called when session closes to ensure no data is lost
118
+ * Returns the usage data that was flushed (for logging)
119
+ */
120
+ async getAndClearUserUsage(userId) {
121
+ const usage = await this.getPendingUsage(userId);
122
+ if (usage) {
123
+ await this.clearUserUsage(userId);
124
+ }
125
+ return usage;
126
+ }
127
+ }
128
+ exports.UsageDataManager = UsageDataManager;
@@ -19,6 +19,7 @@ export declare class RedisClient {
19
19
  smembers(key: string): Promise<string[]>;
20
20
  hset(key: string, data: Record<string, string>): Promise<number>;
21
21
  hgetall(key: string): Promise<Record<string, string>>;
22
+ hincrby(key: string, field: string, increment: number): Promise<number>;
22
23
  zadd(key: string, score: number, member: string): Promise<number>;
23
24
  zrange(key: string, start: number, stop: number): Promise<string[]>;
24
25
  zremrangebyrank(key: string, start: number, stop: number): Promise<number>;
@@ -52,6 +52,9 @@ class RedisClient {
52
52
  async hgetall(key) {
53
53
  return this.client.hgetall(key);
54
54
  }
55
+ async hincrby(key, field, increment) {
56
+ return this.client.hincrby(key, field, increment);
57
+ }
55
58
  async zadd(key, score, member) {
56
59
  return this.client.zadd(key, score, member);
57
60
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mulingai-npm/redis",
3
- "version": "3.29.4",
3
+ "version": "3.29.7",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "repository": {