1+ package HalfFifty .HalfFifty_BE .translation .bean ;
2+
3+ import HalfFifty .HalfFifty_BE .translation .domain .DTO .FrameBufferResult ;
4+ import org .springframework .scheduling .annotation .Scheduled ;
5+ import org .springframework .stereotype .Component ;
6+
7+ import java .time .LocalDateTime ;
8+ import java .util .*;
9+ import java .util .concurrent .ConcurrentHashMap ;
10+ import java .util .function .Function ;
11+ import java .util .stream .Collectors ;
12+
13+ @ Component
14+ public class FrameBufferBean {
15+ private final Map <UUID , BufferData > userFrameBuffers = new ConcurrentHashMap <>();
16+ private final int BUFFER_SIZE = 5 ;
17+ private final int REQUIRED_MATCHES = 3 ;
18+ private final int TIMEOUT_MINUTES = 10 ; // 10분 후 자동 삭제
19+
20+ // 버퍼 데이터와 타임스탬프를 함께 저장하는 내부 클래스
21+ private static class BufferData {
22+ List <String > buffer ;
23+ LocalDateTime lastAccess ;
24+
25+ BufferData () {
26+ this .buffer = new ArrayList <>();
27+ this .lastAccess = LocalDateTime .now ();
28+ }
29+
30+ void updateAccess () {
31+ this .lastAccess = LocalDateTime .now ();
32+ }
33+ }
34+
35+ public FrameBufferResult addFrame (UUID userId , String predictedWord ) {
36+ if (userId == null || predictedWord == null || predictedWord .trim ().isEmpty ()) {
37+ return new FrameBufferResult (false , null , 0 , BUFFER_SIZE );
38+ }
39+
40+ BufferData bufferData = userFrameBuffers .computeIfAbsent (userId , k -> new BufferData ());
41+ bufferData .updateAccess (); // 접근 시간 갱신
42+
43+ List <String > buffer = bufferData .buffer ;
44+ buffer .add (predictedWord .trim ());
45+
46+ // 버퍼 크기 제한
47+ if (buffer .size () > BUFFER_SIZE ) {
48+ buffer .remove (0 );
49+ }
50+
51+ // 다수결 확인
52+ if (buffer .size () >= BUFFER_SIZE ) {
53+ return checkMajority (buffer , userId );
54+ }
55+
56+ return new FrameBufferResult (false , null , buffer .size (), BUFFER_SIZE );
57+ }
58+
59+ private FrameBufferResult checkMajority (List <String > buffer , UUID userId ) {
60+ try {
61+ Map <String , Long > wordCounts = buffer .stream ()
62+ .filter (Objects ::nonNull )
63+ .filter (word -> !word .trim ().isEmpty ())
64+ .collect (Collectors .groupingBy (Function .identity (), Collectors .counting ()));
65+
66+ Optional <Map .Entry <String , Long >> mostFrequent = wordCounts .entrySet ().stream ()
67+ .max (Map .Entry .comparingByValue ());
68+
69+ if (mostFrequent .isPresent () && mostFrequent .get ().getValue () >= REQUIRED_MATCHES ) {
70+ // 확정되면 해당 사용자의 버퍼 초기화
71+ clearBuffer (userId );
72+ return new FrameBufferResult (true , mostFrequent .get ().getKey (), BUFFER_SIZE , BUFFER_SIZE );
73+ }
74+
75+ return new FrameBufferResult (false , null , buffer .size (), BUFFER_SIZE );
76+
77+ } catch (Exception e ) {
78+ System .err .println ("Error in checkMajority: " + e .getMessage ());
79+ return new FrameBufferResult (false , null , buffer .size (), BUFFER_SIZE );
80+ }
81+ }
82+
83+ public void clearBuffer (UUID userId ) {
84+ if (userId != null ) {
85+ userFrameBuffers .remove (userId );
86+ }
87+ }
88+
89+ public List <String > getBuffer (UUID userId ) {
90+ if (userId == null ) {
91+ return new ArrayList <>();
92+ }
93+ BufferData bufferData = userFrameBuffers .get (userId );
94+ return bufferData != null ? new ArrayList <>(bufferData .buffer ) : new ArrayList <>();
95+ }
96+
97+ // 메모리 누수 방지: 10분마다 오래된 버퍼 자동 삭제
98+ @ Scheduled (fixedRate = 600000 ) // 10분마다 실행
99+ public void cleanupOldBuffers () {
100+ LocalDateTime cutoffTime = LocalDateTime .now ().minusMinutes (TIMEOUT_MINUTES );
101+
102+ Iterator <Map .Entry <UUID , BufferData >> iterator = userFrameBuffers .entrySet ().iterator ();
103+ int removedCount = 0 ;
104+
105+ while (iterator .hasNext ()) {
106+ Map .Entry <UUID , BufferData > entry = iterator .next ();
107+ if (entry .getValue ().lastAccess .isBefore (cutoffTime )) {
108+ iterator .remove ();
109+ removedCount ++;
110+ }
111+ }
112+
113+ if (removedCount > 0 ) {
114+ System .out .println ("정리된 비활성 버퍼 개수: " + removedCount );
115+ }
116+
117+ System .out .println ("현재 활성 버퍼 개수: " + userFrameBuffers .size ());
118+ }
119+ }
0 commit comments