<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>MIT 6.5840 Distributed_Systems on Jiho Kim</title><link>https://blog.wlgh7407.com/posts/project/mit_6.5840_distributed_systems/</link><description>Recent content in MIT 6.5840 Distributed_Systems on Jiho Kim</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Wed, 02 Sep 2026 09:56:08 +0900</lastBuildDate><atom:link href="https://blog.wlgh7407.com/posts/project/mit_6.5840_distributed_systems/index.xml" rel="self" type="application/rss+xml"/><item><title>Lab 1 MapReduce</title><link>https://blog.wlgh7407.com/posts/project/mit_6.5840_distributed_systems/260902_project_lab-1-mapreduce/</link><pubDate>Wed, 02 Sep 2026 09:56:08 +0900</pubDate><guid>https://blog.wlgh7407.com/posts/project/mit_6.5840_distributed_systems/260902_project_lab-1-mapreduce/</guid><description>&lt;p&gt;Go 언어로 &lt;a class="link" href="https://blog.wlgh7407.com/posts/dev/260828_dev_mapreduce-simplified-data-processing-on-large-clusters-2004/" &gt;MapReduce&lt;/a&gt;의 논문 내용을 구현해보자. 아래 내용은 실시간성 시행착오&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&amp;hellip;일단 제일 큰 이슈. 뭐부터 해야하지?
&lt;ul&gt;
&lt;li&gt;먼저 &lt;code&gt;rpc.go&lt;/code&gt;에서 요청/응답 형태를 만들고, 응답이 왔을때 Map작업을 하는거까지부터 만들어보자.
&lt;ul&gt;
&lt;li&gt;여기서 &lt;code&gt;ExampleArgs&lt;/code&gt;가 있어서 찾아보니까, 요청/응답을 &lt;code&gt;Args&lt;/code&gt;, &lt;code&gt;Reply&lt;/code&gt;로 하는게 관례인 것 같다.&lt;/li&gt;
&lt;li&gt;따라서 우리도 &lt;code&gt;TaskArgs&lt;/code&gt;, &lt;code&gt;TaskReply&lt;/code&gt; 로 해볼까?&lt;/li&gt;
&lt;li&gt;일단 &lt;code&gt;TaskReply&lt;/code&gt;에서, 무슨 정보가 필요할까?
&lt;ul&gt;
&lt;li&gt;어떤 작업인지 알려줘야하고 (Map or Reduce)
&lt;ul&gt;
&lt;li&gt;여기서 파이선 enum같은게 가능할까? 두 string중 하나로 정리하고싶은데. 아니면 bool로 할까?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;어떤 입력파일을 처리해야할지 알려줘야 하고
&lt;ul&gt;
&lt;li&gt;이건 Map, Reduce 둘다 작업 번호로 처리할 수 있을 것 같다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이 두가지면 될거같은데?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이후 &lt;code&gt;worker.go&lt;/code&gt;에서 이를 받아줘야할 것 같다.
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CallExample&lt;/code&gt;이 예시로 있으니, 우리도 &lt;code&gt;CallTask&lt;/code&gt;로 이름짓고 하면 될듯&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;아 이게 다 붙어있네. &lt;code&gt;CallTask&lt;/code&gt;는 &lt;code&gt;coordinator.go&lt;/code&gt;에서 잘 반환해줘야하는데..
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Coordinator&lt;/code&gt; struct에서 Task의 개수와 어디까지 보냈는지같은걸 잘 관리해야겠다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;실제로 실행하면서 한번 해보자.
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;go run mrcoordinator.go sock123 pg-*.txt&lt;/code&gt; 로 코디네이터를 만들면
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;coordinator.go&lt;/code&gt; 의 &lt;code&gt;MakeCoordinator&lt;/code&gt; 이 호출되고,&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;go run mrworker.go wc.so sock123&lt;/code&gt; 로 워커를 만들면
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;worker.go&lt;/code&gt;의 &lt;code&gt;Worker&lt;/code&gt; 함수가 호출된다.&lt;/li&gt;
&lt;li&gt;구조를 보니 map, reduce 함수는 만들어져있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;두 함수를 잘 채워주자!&lt;/li&gt;
&lt;li&gt;&lt;img
 src="https://blog.wlgh7407.com/images/Pasted%20image%2020260902105223.png"
 alt="Image Description"loading="lazy"&gt;&lt;/li&gt;
&lt;li&gt;일단 동시성 없이, Map요청을 우선으로 응답하고 Reduce연산을 응답하는 구조를 만들었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이제 작업을 붙여야한다.
&lt;ul&gt;
&lt;li&gt;지금 하다보니, &lt;code&gt;ExampleReply&lt;/code&gt;에서 &lt;code&gt;filename&lt;/code&gt;도 필요할 것 같다.&lt;/li&gt;
&lt;li&gt;이후 &lt;code&gt;mrsequential.go&lt;/code&gt;에서 하는것과 같이 file을 읽어들여서 &lt;code&gt;mapf&lt;/code&gt; 함수를 먹이면, 다음과 같이 잘 수행됨을 확인할 수 있다!&lt;/li&gt;
&lt;li&gt;&lt;img
 src="https://blog.wlgh7407.com/images/Pasted%20image%2020260902132530.png"
 alt="Image Description"loading="lazy"&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이제 이 결과를 nReduce개의 중간 파일에 저장하자.
&lt;ul&gt;
&lt;li&gt;아잇;; NReduce도 RPC에서 전달받아야했다.&lt;/li&gt;
&lt;li&gt;&lt;img
 src="https://blog.wlgh7407.com/images/Pasted%20image%2020260902135414.png"
 alt="Image Description"loading="lazy"&gt;&lt;/li&gt;
&lt;li&gt;잘 데려와서 진행하면, 중간 버킷을 이용해서 위와 같이 &lt;code&gt;mr-M-R&lt;/code&gt; 파일명으로 모두 저장해줄 수 있다!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Reduce 함수도 같은 방식으로 만들어주면 된다.
&lt;ul&gt;
&lt;li&gt;&lt;img
 src="https://blog.wlgh7407.com/images/Pasted%20image%2020260902141712.png"
 alt="Image Description"loading="lazy"&gt;&lt;/li&gt;
&lt;li&gt;깔끔하게 10개의 out파일이 잘 나온다!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이제 자랑스럽게 &lt;code&gt;make mr&lt;/code&gt;을 돌려보면, 다음과같은 오류들을 볼 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;WARNING: DATA RACE
Read at 0x00c00013f1f0 by main goroutine:
 6.5840/mr.(*Coordinator).Done()
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;이는 채점 환경이 Worker을 3개 돌리는 고루틴 환경이기 때문!
&lt;ul&gt;
&lt;li&gt;우리는 이제 동시성도 제어해야한다.&lt;/li&gt;
&lt;li&gt;mutex같은걸로 보호하면서 수행해보자.&lt;/li&gt;
&lt;li&gt;뭘 보호해야하지?&lt;/li&gt;
&lt;li&gt;DATA RACE가 나는 상황을 보면, TaskRPC에서 Coordinator을 참조할때 문제가 생기는 것 같다.
&lt;ul&gt;
&lt;li&gt;따라서 TaskRPC 하나가 돌동안 잠궈주면 되지 않을까?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;여기까지 잘 하면 &lt;code&gt;--- PASS: TestWc (7.95s)&lt;/code&gt; 를 일단 받을 수 있었다!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이제 문제는 실패한 태스트 혹은 할당의 문제..
&lt;ul&gt;
&lt;li&gt;현재 구조에서는 한번 보낸 태스크들은 모두 버그없이 완료되었다고 생각한다.
&lt;ul&gt;
&lt;li&gt;Midx정도로만 관리했기 때문&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이제 현재 업무가 어떤 상태인지도 정의해야할 것 같은데..
&lt;ul&gt;
&lt;li&gt;두가지 아이디어가 생각난다.
&lt;ul&gt;
&lt;li&gt;첫번째는 배열/슬라이스로, $[M_1, M_2, M_3, ..., M_{m-1}, M_M]$ 에 대해 각 $(0, 1, 2)$ 같은 값을 부여해서, 할당 없음 / 작업중 / 작업 완료 세가지로 나누는 것이다!&lt;/li&gt;
&lt;li&gt;두번째는 set.. 이지만 Go에는 없으므로 map같은걸로 위의 세가지 그룹에 대한 집합을 관리하는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;뭐가됐든 RPC는 하나가 더 필요할 것 같다. 성공을 관리하는거!&lt;/li&gt;
&lt;li&gt;그리고 set은 어떻게 구현할지 바로 생각이 안나니까 일단 슬라이스로 시작해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;슬라이스로 관리하고, 함수 하나를 정의해야해서 일이 있으면 해당 인덱스, 할일은 없지만 작업중인 일이 남아있으면 -1, 모든 일이 끝났으면 -2를 반환하도록 하였다.
&lt;ul&gt;
&lt;li&gt;추가적으로 실수가 있었던 kv레코드 / json인코딩 저장 방식까지 수정하니까 (공식 가이드에있었음) 다음과 같이 꽤 좋은 결과를 얻을 수 있었다!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;=== RUN TestWc
--- PASS: TestWc (10.08s)
=== RUN TestIndexer
--- PASS: TestIndexer (5.87s)
=== RUN TestMapParallel
--- PASS: TestMapParallel (8.04s)
=== RUN TestReduceParallel
--- PASS: TestReduceParallel (10.05s)
=== RUN TestJobCount
--- PASS: TestJobCount (12.06s)
=== RUN TestEarlyExit
cmp: EOF on mr-wc-initial.txt
 util.go:163: reduce output changed: mr-wc-initial.txt vs mr-wc-final.txt
--- FAIL: TestEarlyExit (7.05s)
=== RUN TestCrashWorker
^Csignal: interrupt
FAIL 6.5840/mr 263.011s
make: *** [mr] Error 1
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;아마도 EarlyExit, CrashWorker 둘다 이름에서 알 수 있다시피 내결함성에 관한걸 테스트해야할 것 같다.
&lt;ul&gt;
&lt;li&gt;만약에 worker이 일을 물고 죽었다면? 어떡하지? 같은거지.&lt;/li&gt;
&lt;li&gt;이것도 사이트를 보면 10초정도 후에 백업 태스크가 살아야한다고 알려준다. 이를 구현해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;저걸 해서 CrashWorker까진 고쳤지만 EarlyExit이 안돈다! 이게 무슨 일일까?
&lt;ul&gt;
&lt;li&gt;EarlyExit은 worker가 Task를 받자마자 죽어버리는거같은 일이다.
&lt;ul&gt;
&lt;li&gt;그런데 아예 죽지 않고 살아있다면?&lt;/li&gt;
&lt;li&gt;그래서 동시에 두 Worker이 같은 일을 하고있어서, &lt;code&gt;os.Create&lt;/code&gt;를 같이 써버린다면?
&lt;ul&gt;
&lt;li&gt;분명 내용이 유실되겠지!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;그렇다면, 임시파일에 쭈루룩 쓰다가 모두 쓴 다음에 &lt;code&gt;os.Rename&lt;/code&gt;같은걸 이용해보자.
&lt;ul&gt;
&lt;li&gt;이거 문제가 아닌데..&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;아니;; 다른 원인을 찾다보니 Reduce 작업에서 조금 실수해서 완료처리를 잘못하고 있었다.
&lt;ul&gt;
&lt;li&gt;이걸 고치고 깔끔하게 완료!
&lt;img
 src="https://blog.wlgh7407.com/images/Pasted%20image%2020260903211357.png"
 alt="Image Description"loading="lazy"&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;고친후에 깔끔히 만점!&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="알게된-사실들"&gt;&lt;a href="#%ec%95%8c%ea%b2%8c%eb%90%9c-%ec%82%ac%ec%8b%a4%eb%93%a4" class="header-anchor"&gt;&lt;/a&gt;알게된 사실들
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;struct 내부의 메서드를 대문자로 시작하지 않으면 다른데서 접근조차 안된다! 저장부터 안들어가는듯&lt;/li&gt;
&lt;li&gt;Mutex Lock를 걸면 자동으로 해당 데이터에 대한 접근을 막는게 아니라, 다음 Mutex의 Lock까지를 대기한다.
&lt;ul&gt;
&lt;li&gt;따라서 쓰기에서만 Lock을 걸면 안되고, 읽을때도 하나하나 다 걸어줘야한다!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;함수 &lt;code&gt;func1&lt;/code&gt;이 함수 &lt;code&gt;func2&lt;/code&gt;를 호출하는 경우, 그리고 둘다 &lt;code&gt;c&lt;/code&gt;를 사용한다고 생각해보자.
&lt;ul&gt;
&lt;li&gt;이때 둘다 Mutex Lock을 걸어버리면, Deadlock이 발생한다.&lt;/li&gt;
&lt;li&gt;따라서, 이럴때는 내부 헬퍼함수같은 느낌으로 진행할 수 있겠다.
&lt;ul&gt;
&lt;li&gt;이때 헬퍼함수는 뒤에 Locked같은 접미사를 붙이는게 관례라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;공식 가이드에도 있는 내용인데, json으로 저장하기 위해선 k-v 텍스트 레코드대신 json 인코딩된 레코드로 저장하는게 좋다고 한다. 왜지?
&lt;ul&gt;
&lt;li&gt;텍스트 포맷은 구분자에 의존한다 (개행)&lt;/li&gt;
&lt;li&gt;그런데 Key나 Value에 이미 그런 구분자가 들어있다면?
&lt;ul&gt;
&lt;li&gt;따라서 json형태로 저장하면 훨씬 안전하다!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>