1- // nsfwDetection/nsfwProcess.js
21import { fork } from 'child_process' ;
32import path from 'path' ;
3+ import os from 'os' ;
44
55const nsfwServicePath = path . resolve ( 'nsfwDetection/nsfwService.cjs' ) ;
6- const nsfwProcess = fork ( nsfwServicePath , [ ] , { stdio : [ 'pipe' , 'pipe' , 'pipe' , 'ipc' ] } ) ;
6+ const WORKER_COUNT = os . cpus ( ) . length ; // Use number of CPU cores
7+ const WORKER_TIMEOUT = 5000 ; // 5 seconds per request
78
8- let ready = false ;
9- const pending = new Map ( ) ;
9+ let workers = [ ] ;
10+ let readyWorkers = new Set ( ) ;
11+ let pending = new Map ( ) ; // Map: id -> { resolve, reject, timeout, workerIdx }
12+ let rrIndex = 0 ; // Round-robin index
1013
11- nsfwProcess . on ( 'message' , ( msg ) => {
12- if ( msg . type === 'ready' ) ready = true ;
13- if ( msg . type === 'result' && pending . has ( msg . id ) ) {
14- // Log predictions from the child process
15- if ( msg . predictions ) {
16- console . log ( 'NSFW Predictions:' , msg . predictions ) ;
14+ function spawnWorker ( idx ) {
15+ const worker = fork ( nsfwServicePath , [ ] , { stdio : [ 'pipe' , 'pipe' , 'pipe' , 'ipc' ] } ) ;
16+ worker . on ( 'message' , ( msg ) => {
17+ if ( msg . type === 'ready' ) {
18+ readyWorkers . add ( idx ) ;
1719 }
18- pending . get ( msg . id ) ( msg . safe ) ;
19- pending . delete ( msg . id ) ;
20- }
21- if ( msg . type === 'error' && pending . has ( msg . id ) ) {
22- pending . get ( msg . id ) ( false , msg . error ) ;
23- pending . delete ( msg . id ) ;
24- }
25- } ) ;
20+ if ( msg . type === 'result' && pending . has ( msg . id ) ) {
21+ const { resolve, timeout } = pending . get ( msg . id ) ;
22+ clearTimeout ( timeout ) ;
23+ resolve ( msg . safe ) ;
24+ pending . delete ( msg . id ) ;
25+ }
26+ if ( msg . type === 'error' && pending . has ( msg . id ) ) {
27+ const { reject, timeout } = pending . get ( msg . id ) ;
28+ clearTimeout ( timeout ) ;
29+ reject ( new Error ( msg . error ) ) ;
30+ pending . delete ( msg . id ) ;
31+ }
32+ } ) ;
33+ worker . on ( 'exit' , ( ) => {
34+ readyWorkers . delete ( idx ) ;
35+ // Clean up any pending tasks for this worker
36+ for ( const [ id , task ] of pending . entries ( ) ) {
37+ if ( task . workerIdx === idx ) {
38+ clearTimeout ( task . timeout ) ;
39+ task . reject ( new Error ( 'NSFW worker crashed' ) ) ;
40+ pending . delete ( id ) ;
41+ }
42+ }
43+ // Restart worker
44+ setTimeout ( ( ) => {
45+ workers [ idx ] = spawnWorker ( idx ) ;
46+ } , 2000 ) ;
47+ } ) ;
48+ worker . on ( 'error' , ( ) => {
49+ readyWorkers . delete ( idx ) ;
50+ } ) ;
51+ return worker ;
52+ }
53+
54+ // Initialize worker pool
55+ for ( let i = 0 ; i < WORKER_COUNT ; i ++ ) {
56+ workers . push ( spawnWorker ( i ) ) ;
57+ }
2658
2759export function isNsfwReady ( ) {
28- return ready ;
60+ return readyWorkers . size > 0 ;
2961}
3062
3163export function checkNsfw ( imagePath ) {
3264 return new Promise ( ( resolve , reject ) => {
33- if ( ! ready ) return reject ( new Error ( 'NSFW service not ready' ) ) ;
65+ if ( readyWorkers . size === 0 ) return reject ( new Error ( 'NSFW service not ready' ) ) ;
66+ // Pick next ready worker in round-robin
67+ let tries = 0 ;
68+ let workerIdx = rrIndex ;
69+ while ( ! readyWorkers . has ( workerIdx ) && tries < WORKER_COUNT ) {
70+ workerIdx = ( workerIdx + 1 ) % WORKER_COUNT ;
71+ tries ++ ;
72+ }
73+ if ( ! readyWorkers . has ( workerIdx ) ) {
74+ return reject ( new Error ( 'No NSFW workers ready' ) ) ;
75+ }
76+ rrIndex = ( workerIdx + 1 ) % WORKER_COUNT ;
3477 const id = Date . now ( ) + Math . random ( ) ;
35- nsfwProcess . send ( { type : 'check' , imagePath, id } ) ;
36- pending . set ( id , ( safe , error ) => {
37- if ( error ) return reject ( new Error ( error ) ) ;
38- resolve ( safe ) ;
39- } ) ;
78+ try {
79+ workers [ workerIdx ] . send ( { type : 'check' , imagePath, id } ) ;
80+ const timeout = setTimeout ( ( ) => {
81+ pending . delete ( id ) ;
82+ reject ( new Error ( 'NSFW check timed out' ) ) ;
83+ } , WORKER_TIMEOUT ) ;
84+ pending . set ( id , { resolve, reject, timeout, workerIdx } ) ;
85+ } catch ( err ) {
86+ pending . delete ( id ) ;
87+ reject ( new Error ( 'Failed to send image to NSFW service' ) ) ;
88+ }
4089 } ) ;
41- }
90+ }
0 commit comments