그냥 어떻게든 해보고 싶다! 라는 마음이 들었던 동시성 이벤트를 해결해보려고 했다.
근데 여기서 다른 분들이 이것저것 이야기를 해주셨다.
정말 고려해야할 것이 말도 안되게 많은 것이였다(.....)
몇 분은 그냥....추첨식으로 하는게 좋다고....^^.....
선착순하면 서버터지고 고객센터 폭주하고 별 일이 다 생긴다고 이야기를 해주셨다.
그래도 뭔가 해보고는 싶어서 그냥 한번 건드려봤다.
지금까지는 그냥 마우스로 광클하는걸로 부하테스트(...)를 하고 있었는데, JMeter를 통해서 부하테스트를 할 수 있게 됐다.
GraphQL에서는 사용법을 정말 이해를 못하겠어가지고 답답해하고 있었는데
REST API로 바꾸니까 생각보다 쉽게 해결이 됐다.
1초에 1000번도 쏴보고 정말 별거 다해봤다(?)
이제는 뭔가 입력값을 서로 다르게 보내야하는 그런걸 어떻게 해야하는지 알아보면 좋을 것 같다.
직접 다 바꿔서 적는거야 할 수 있긴 한데..... 뭔가 어떻게든 방법이 있지 않을까 싶어서?
나는 데이터베이스가 느리다고 생각했다.
위의 조건을 그냥 실행시킬경우 더티리드가 발생해서 쿠폰을 등록한 사람은 100명이 나오는데
쿠폰의 재고는 90개 이상이 남는 일이 벌어졌기 때문인데(....)
이건 데이터베이스가 느린게 아니라 그냥 비동기여서 발생하는 문제같기도 하고
내가 생각하는 것처럼 레디스라는 메모리DB를 사용하는 것 보다는 DB로도 대부분 해결할 수 있다는 이야기
도 함께 해주셨다.
그리고 엘라스틱서치에 관한 이야기가 나왔는데
내가 부트캠프에서 배울 때는, 수백만까지는 일반 디비를 써도 충분히 빠르기 때문에 그 이상의 데이터에서 사용한다.
라는 이야기를 들었다.
그런데 스페이스에서 이런 이야기를 해드렸더니 용도에 맞게 사용하는 것이지, 데이터의 양에 따라 결정되는 것이 아니다. 라고 알려주셨다.
예를 들어, 복잡한 내용을 검색하는 것이 필요하다면 데이터의 양이 100개정도 밖에 없더라도
분석기를 통한 검색이 가능한 엘라스틱서치를 사용하는게 맞다는 것.
많은 사람들이 기술(스택)을 정할 때 데이터의 양을 기준으로 사용하는 이유를 말한다고 하는데
데이터의 양이 아니라, 목적
에 따라서 새로운 기술의 도입을 결정하는 것이라고 알려주셨다.
면접에서 질문도 받아봤지만 사용해보지 못했던 스코프를 직접 써보게 됐다.
왜?
발버둥이라도 쳐보려고, 뭐라도 되면 좋겠다 싶어서.
뭐를 써봤냐면 위에 있는 리퀘스트스코프를 써봤다.
들어오는 요청을 한개로 다 묶어버린다면, 값이 틀어지지 않을거야! 라는 마음 하나로.
는 개뿔 요청이 서로 각기 다른 곳에서 오는 것이라 그냥 다 달랐다(....)
왼쪽
은 설정값이 없는 디폴트 스코프, 오른쪽
은 리퀘스트 스코프를 적용해놓았을 때의 this.data의 결과값
의이다.
저 this.data를 호출하는 비즈니스 로직에는 1회 호출당 this.data +=1이라는 연산이 들어가있다.
디폴트 스코프는 싱글톤
을 지원하다보니, 요청이 서로 다르더라도 한개의 서버에서 작동하는 것이라
값이 증가하는 것을 확인할 수 있고
리퀘스트 스코프는 각 각의 호출이 새로운 인스턴스를 생성하는 것이라
1이라는 값만 출력되는 것을 볼 수 있었다.
써보면 안다고 하더니, 써봤더니 바로 이놈의 사용 목적을 어느정도 알았다고 해야할까?
한번 호출을 할 때, 여러가지 작업이 로직이 이루어지고 다른것에 의하여 값이 절대로 틀어지면 안되는 일이 생길 경우 사용을 하면 될 것 같다.
마치 결제같은 비즈니스 로직을 짤 때 좋은 것 같기도하고...
아무튼 저거는 내가 해결하려고 했던 이슈에서는 전혀 쓸모가 없었다
땡!
저 조건을 올리자마자 수많은 분들이 오셔가지고 이것저것 이야기를 많이 던져주셨는데
결국은 서버에 직접 접근하는 것이 아니라, 메세지큐를 앞에 붙이는 것이 좋다.
라는 결론으로 이어졌다.
왜냐하면 로직이라는 것은 한번 짜놓고 여러번 재사용을 할 수 있기 때문에, 최대한 안정성을 갖추는 것이 좋은 코드라고 하셨고
그렇기 때문에 나중을 위해서라도 한번 짤 때 제대로 짜놓는 것이 좋기에. 메세지큐가 없더라도 만들 수는 있지만 도입하는게 좋다고 하셨다.
그렇게 받은 여러가지 조언들
고려를 해야하는 것이 정말 많았던 기능이였다.
예로부터 선착순 이벤트는 서버가 폭발해서 유저들이 쌍욕박는게 일상이였는데
그걸 이 조막만한 지식으로 건드려보려고 했으니 힌트를 주시는 것 마다 알아먹을 수 있는게 하나도 없었다(....)
람다 아키텍처, 람다, AWS 람다에 대해서도 한번 알아봐야할 것 같고....
요청하는 시간을 위변조 할 수 있다는 생각도 못해서 저런 검증도 생각을 해봐야할 것 같다.
그래도 내 지식 수준에서는 모래성을 쌓아보고 싶어서 고민을 해가지고 어느정도 실행까지 해봤다.
아직 메세지큐를 사용해본 적이 없다보니, 그저 비슷하게 구현하는 것이 목표여서 나왔던 결론이 아래와 같았다.
결국은 레디스를 큐처럼 사용하는 형태
가 되어버리긴 했다ㅋㅋ
문제는 이벤트리스너를 어떻게 달아야하는지 모르겠어서 데이터 저장까진 못해봤다(...) 안 X 못 O
심지어 같은 키에 값을 추가하는 것도 몰라서 전역변수의 숫자가 key로 저장되고
값은 쿠폰과 이메일로 들어가있는 것을 볼 수 있다..^^ 숫자는 콘솔로 같이 찍어놓은 것이다.
근데 여기서도 수많은 문제가 있었으니.....
선배님 : 오홍 저 data 변수가 자동으로 atomic 하게 설정되나봐요? += 연산자는 해당 값을 '읽어와서' 조작 후 '쓰는' 과정인데, 읽어올때랑 쓸때 다른 스레드간 경합이 없나봐요?
나 : 비동기를 달아놓지 않고, 노드가 싱글스레드라서 그런가 영향을 받지 않더라구요.
그냥 생각?
아토믹하게 설정된다는게 무슨 말인가 생각을 해봤는데, 결국은 싱글스레드라 영향을 주지 않는다 생각했다.
정말 맞았음!
선배님 : 전역변수라고 적으셨는데, 물리서버가 두 대 이상이라면요?
나 : 예... 망합니다... 제 생각에는 물리서버가 두 대 이상이라면 서버 앞단에 무언가를 달아야하는게 맞는 것 같아요(이게 메세지큐가 될 것 같다.)
선배님 : 1번과 2번 명령 사이의 간격은 어떻게 되나요? 문제가 없을까요?
나 : 한개는 클래스 내부의 싱글톤이고, 한개는 비동기라 괜찮을 것 같긴 합니다.
선배님 : 상상하지말고 문서를 읽어봐요. https://redis.io/commands/rpush/#return
결론
Redis의 append 명령을 쓰는게 더 좋지 않을까요?
나 : 제가 쓰는 라이브러리에는 아무리 찾아봐도 없더라구요. 아래처럼 보이는 주석이 달려있긴 한데, 이게 타입스크립트는 지정이 안되어있어서 좀 알아봐야할 것 같아요.
선배님 : 이거 한번 알아봐요 https://github.com/redis/node-redis
결국 내가 가지고 있던 생각으로는 한계가 명확한 것이였다.
그렇지만 적어도 레디스의 명령어들만 자유롭게 사용할 수 있기만 한다면, 대부분 해결이 될 것 같기도 해서....
아니 왜 리스트가 없냐고요 진짜 미치겠네(....)
저것에 대해서 조금 더 알아보는 시간을 가진다면 얼추 비슷하게 구현을 할 수 있지 않을까?
그리고 이벤트리스너만 제대로 찾아가지고 적용시키면 아마도? 잘?? 비스무레하게 구현을 할 수 있을 것 같다.
사실 여기서 더 해보는 것도 도움이 될 것이라 생각하지만, 내 지식의 한계점으로는 해결하기 쉬운 문제가 아닌 것 같아서
이쯤에서 내려놓고 알고리즘 공부하다가 조금 더 나의 지식이 쌓이고나면 다시 한번 시도를 해봐야겠다.
끝!