Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
DMM.go #11 - sync.Condの使い所について考えてみる
Search
na2na
September 17, 2025
Technology
0
10
DMM.go #11 - sync.Condの使い所について考えてみる
DMM.go #11にて使用したスライドです
https://dmm.connpass.com/event/363839/
na2na
September 17, 2025
Tweet
Share
More Decks by na2na
See All by na2na
Kubernetes Meetup Tokyo #69 LT - PreStopによるSleep中に何が起きているか:~安全なRollingUpdateの実施のために~
na2na
2
750
OAuth 2.1 + PKCEのススメ ~Spotify APIを通して理解する、OAuth 2.1 + PKCEの基礎と実践~
na2na
3
830
Oracle Cloudで自宅クラウド構築:ブロックボリュームのスループット改善編
na2na
0
82
Other Decks in Technology
See All in Technology
テストを軸にした生き残り術
kworkdev
PRO
0
210
バイブスに「型」を!Kent Beckに学ぶ、AI時代のテスト駆動開発
amixedcolor
2
580
20250910_障害注入から効率的復旧へ_カオスエンジニアリング_生成AIで考えるAWS障害対応.pdf
sh_fk2
3
270
「何となくテストする」を卒業するためにプロダクトが動く仕組みを理解しよう
kawabeaver
0
430
Oracle Cloud Infrastructure IaaS 新機能アップデート 2025/06 - 2025/08
oracle4engineer
PRO
0
110
Generative AI Japan 第一回生成AI実践研究会「AI駆動開発の現在地──ブレイクスルーの鍵を握るのはデータ領域」
shisyu_gaku
0
330
プラットフォーム転換期におけるGitHub Copilot活用〜Coding agentがそれを加速するか〜 / Leveraging GitHub Copilot During Platform Transition Periods
aeonpeople
1
230
Terraformで構築する セルフサービス型データプラットフォーム / terraform-self-service-data-platform
pei0804
1
190
「Linux」という言葉が指すもの
sat
PRO
4
140
品質視点から考える組織デザイン/Organizational Design from Quality
mii3king
0
210
2025/09/16 仕様駆動開発とAI-DLCが導くAI駆動開発の新フェーズ
masahiro_okamura
0
120
AIのグローバルトレンド2025 #scrummikawa / global ai trend
kyonmm
PRO
1
310
Featured
See All Featured
The Language of Interfaces
destraynor
161
25k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.9k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
15
1.7k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
507
140k
Building an army of robots
kneath
306
46k
Raft: Consensus for Rubyists
vanstee
140
7.1k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
4k
Making the Leap to Tech Lead
cromwellryan
135
9.5k
Building a Modern Day E-commerce SEO Strategy
aleyda
43
7.6k
Bash Introduction
62gerente
615
210k
4 Signs Your Business is Dying
shpigford
184
22k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
30
9.7k
Transcript
sync.Condの使い所に ついて考えてみる DMM.go #11 合同会社DMM.comプラットフォーム開発本部 なずな (@na2na_chang)
自己紹介 なずな(@na2na_chang) • 合同会社DMM.com(2024年新卒) ◦ プラットフォーム開発本部 認可チーム • 認可プロダクトを運用するお仕事 •
最近の悩み: Goのエコシステムの中で完結するE2Eテストの作り方 2
はじめに OSSのコードリーディング、してますか? 私はGoにそこまで明るくないので頑張ってソースコードリーディングをして 「へー、こういうのあるんだー」と勉強をしていたりします。 今日はKubernetesのコードを読んでいた中で見つけた ある発見についてお話しします。 3
今日の発表をおいしく聞ける人 Goroutineは知っているけどsyncパッケージはよく知らない人 4
お品書き 1. 今回のお題を見つけたきっかけ 2. sync.Condとは? 3. チャネルとは? 4. 両者の特性の違い 5.
実際に使っている例を見て、なぜ sync.Condにしたのか考える 5
• sync.Condの存在を知る • チャネルの存在を知る • どういうときにチャネルを使うかを知る • どういうときにsync.Condを使うかを知り、使ってみたくなる 今日のゴール 6
見つけたきっかけ Kubernetesには、 リソースの変更を検知するための SharedIndexInformerという仕組みがあります。 昨年、チームでの業務中に見かけて存在は知っている状態でした。 名前がカッコいいのと、DMM.goのお題にちょうど良かったので深掘りをしました。 内部実装を見ていた時に、今回紹介する sync.Condを見つけた次第です。 以前の調査の結果は Kubernetes
Meetup Tokyo #69 LT - PreStopによるSleep中に何が起きているか:~安全なRollingUpdateの実施のために~ で紹介しています。興味があればそちらもぜひ! 7
今日の登場人物 • sync.Cond • チャネル 8
sync.Condについて知る sync.Condは「条件変数」と呼ばれる同期プリミティブです。 sync.Condの主目的は、 「特定の条件が満たされるまで、ゴルーチンを効率的に待機させること 」です。 CPUを無駄遣いするポーリングをせずに、「準備ができたので確認をしてほしい」 と状態の変化を通知するための道具とイメージすると良いでしょう。 9
sync.Condについて知る sync.Condは、必ずsync.Mutexのような ロックと一緒に使います。 Wait()を呼ぶと、自動でロックを解除して 他のゴルーチンが状態を変更できるようにします。 起こされたら再度ロックを取得してから 処理を再開する、という一連の流れが重要です。 10
sync.Condについて知る sync.Condの実装はチャネルとは対照的で、 非常にシンプルです。 sync.Cond自体はロックを持ちません。 ユーザーが外部から渡したLockerを使い、 待機と通知の機能はランタイムの notifyListにほぼすべてを委譲しています。 11 https://github.com/golang/go/blob/6e676ab2b809d46623acb 5988248d95d1eb7939c/src/sync/cond.go#L40-L43
sync.Condについて知る Wait()メソッドの内部は、 その役割を明確に示しています。 ここにはデータコピーやバッファ管理の ロジックは一切ありません。 12 https://github.com/golang/go/blob/6e676ab2b809d46623acb598824 8d95d1eb7939c/src/sync/cond.go#L67-L73
sync.Condについて知る ユーザーが管理するロックと連携して、 ゴルーチンをWait・WakeUpさせることに 特化しています。 つまり、関心事としては状態の変化であり、 データのやり取りにないことがわかります。 13 https://github.com/golang/go/blob/6e676ab2b809d46623acb598824 8d95d1eb7939c/src/sync/cond.go#L67-L73
チャネルとは チャネルは ゴルーチン間で値を受け渡しするために使用する型です。 14
チャネルとは 調べ始める前のわたし 「チャネルって並行処理で出てくるよな、、、」 「sync.Condも並行処理の文脈で出てきたな、、、」 「なんでこの実装はチャネルにしなかったんだろう 🤔」 「どちらも同じような処理できるだろうし、 Easyに使えるチャネルでいいんじゃない?」 注) 調べ始めてからそもそも比較対象になるような代物ではないことに気づきました。あくまでも調べ始める前の認識です、、、
15
チャネルとは > 「どちらも同じような処理できるだろうし、 Easyに使えるチャネルでいいんじゃない?」 調べていく中で、そもそもチャネルと sync.Condは使用目的の異なる別物で、 並列で語るのは少し変だったことに気づきました。 16
チャネルとは ざっとこんな感じです sync.Cond: ひとつの変化しうる状態を多数のゴルーチンで共有するような用途で使う チャネル : ひとつのゴルーチンから別のゴルーチンにデータを送るための用途で使う 内部実装も見つつ説明をします 17
チャネルとは チャネルにおいて重要なのは、送受信のタイミングを同期させる点です。 基本となるバッファなしチャネルでは、 送受信の相手がいなければ処理がブロック(一時停止)します。 18
チャネルとは 一方、バッファ付きチャネルは、チャネル内に指定した数のデータを保持できるため、 バッファが満杯になるまで送信を、バッファが空になるまで受信をブロックしません。 これにより、送信者と受信者の処理をある程度非同期に進めることができます。 19
チャネルとは どちらのチャネルでも、このブロックする性質のおかげで、 開発者がロックを細かく制御しなくても、ゴルーチン間の同期が自然と成立します。 これはGoの哲学である『通信によってメモリを共有する』 を体現した機能と言えると思います。 20
チャネルについて知る チャネルの実体は、ランタイムにある hchanという巨大な構造体です。 https://github.com/golang/go/blob/6e676ab2b809d46623acb5988248d95d1eb7939c/src/runtime/chan.go#L34-L55 21
チャネルについて知る 送信処理(chansend)を例にデータ転送の仕組みを見ていきます ch <- dataというシンプルな操作の裏側で、 大きく分けて二つのパスを辿ります。 • 高速パス (Fast Path):
ロック不要の受け渡し • 低速パス (Slow Path): ロック必須のデータ受け渡し 22
チャネルについて知る 高速パス (Fast Path): ロック不要の受け渡し バッファなしチャネル、またはバッファが空のチャネルに送信しようとした際、 すでに行列(recvq)で待っている受信ゴルーチンがいた場合に使われます。 https://github.com/golang/go/blob/6e676ab2b809d46623acb5988248d95d1eb7939c/src/runtime/chan.go#L229-L234 23
チャネルについて知る 低速パス (Slow Path): ロック必須のデータ受け渡し 高速パスが失敗した = すぐにはデータを受け取ってくれる相手がいない場合に使われます 以下の2パスに分岐します。 •
バッファに空きがある場合 • バッファが満杯の場合 24
チャネルについて知る バッファに空きがある場合 データをリングバッファにコピーし、カウンタ (qcount)とインデックス(sendx)を更新します。 仕事は終わったので、unlock(&c.lock)でロックを解放して終了です。 https://github.com/golang/go/blob/6e676ab2b809d46623acb5988248d95d1eb7939c/src/runtime/chan.go#L229-L234 25
チャネルについて知る バッファも満杯の場合 送信ゴルーチンは自身の情報を送信待ちキュー (sendq) に追加し、 gopark を呼び出してスリープ状態に入ります。 この時、チャネルのロックは自動的に解放され、 他のゴルーチンがチャネルを操作できるようになります。 https://github.com/golang/go/blob/6e676ab2b809d46623acb5988248d95d1eb7939c/src/runtime/chan.go#L236-L309
26
チャネルについて知る ここまでの内容から、 チャネルはあくまでデータのやり取りが主たる関心事であることがわかります 27
わかること 28 sync.Cond (条件変数) chan (チャネル) 関心事 共有されている「状態」が変化したこと。 「データ」そのものを、あるゴルーチンから別のゴ ルーチンへ送ること。
役割 状態の変化を待っているゴルーチン全員に 「状態が変わったかもしれないから、確認してくだ さい」と知らせる。 データを特定のゴルーチンに 直接、安全に届ける。 その後 起こされたゴルーチンたちは、ロックを 再取得できたものから一つずつ処理をする データを受け取ったゴルーチンは、 そのデータに対する所有権を得て、 他のゴルーチンと競合することなく 処理を開始できる
わかること ここからは、実際にsync.Condを使っているアプリケーションコードを見ていきます。 今回はsync.Condを知るきっかけになったSharedIndexInformerをお題にします。 29
SharedIndexInformerとは k8s.io/client-goのSharedIndexInformerは、 Kubernetesリソースの変更を効率的に監視し、その状態を共有キャッシュとして 保持するための中心的な仕組みです。 30
SharedIndexInformerとは 例えばKubernetesを用いてデプロイされている Goで作られたバックエンドアプリケーションの更新するケースを考えてください Deploymentのイメージタグを更新後のアプリケーションのイメージタグに書き換えます 31
SharedIndexInformerとは 32 Kubernetesは、Deploymentの内容が書き変わったことを検知して、 最終的には更新後のアプリケーションのイメージを使った Podが立ち上がることになります SharedIndexInformerは、 この「Deploymentの内容が書き変わったイベントを検知」するための仕組みを支えています
SharedIndexInformerとは 今は例としてDeploymentを出しましたが、 基本的にはKubernetesで扱う全てのリソースの変更が この仕組みで検知されています。 33
SharedIndexInformerの仕組み おおまかに以下のようなコンポーネントで構成されています。 • APIサーバーとの通信 • キュー • ローカルキャッシュ 34
SharedIndexInformerの仕組み 以下を実現するためにキューの仕組み (k8s.io/client-goのcache.DeltaFIFO)を持っており、 この中でsync.Condを利用しています。 効率性: 無駄なイベントを集約し、コントローラーの負荷を減らす。 正確性: 特に削除イベントを確実に捉え、状態変化の「差分」を管理する。 信頼性: 監視が途切れても再同期によって状態の矛盾を防ぐ。
35
SharedIndexInformerの仕組み 今回はk8s.io/client-goのcache.DeltaFIFOを例に、 どう言った時にsync.Condを使うのがいいかを見ていこうと思います。 36
DeltaFIFO k8s.io/client-goのcache.DeltaFIFOは、キューの役割をします。 https://github.com/kubernetes/client-go/blob/master/tools/cache/delta_fifo.go 37
DeltaFIFO キューの実態は以下の二つです • items • queue これをlock(sync.RWMutex)で ロックしてデータの整合性を守っています 38 https://github.com/kubernetes/client-go/blob/master/tools/cache/delta_fifo.go
DeltaFIFO cond sync.Condの部分で、 チャネルではなく条件変数を使っています 39 https://github.com/kubernetes/client-go/blob/master/tools/cache/delta_fifo.go
DeltaFIFO forループの中で、 キューが空の間、f.cond.Wait()を 呼び出しています cond.Wait()はcond.Lockerを 自動的にアンロックします。 その後、通知が来るまでゴルーチンを ブロックします 40 https://github.com/kubernetes/client-go/blob/master/tools/cache/delta_fifo.go
DeltaFIFO データを追加する側では 処理の一番最後に f.cond.Broadcast()を呼んでいます 41 https://github.com/kubernetes/client-go/blob/master/tools/cache/delta_fifo.go
DeltaFIFO データを追加する側では 処理の一番最後に f.cond.Broadcast()を呼んでいます 42 https://github.com/kubernetes/client-go/blob/master/tools/cache/delta_fifo.go
DeltaFIFO f.cond.Broadcast()が呼ばれると、 cond.Wait()で待機していた全ての ゴルーチンがwake upします。 43 https://github.com/kubernetes/client-go/blob/master/tools/cache/delta_fifo.go
なぜDeltaFIFOはsync.Condを選んだのか? DeltaFIFOの内部を見てきたところで、 なぜDeltaFIFOはsync.Condを選んだのかを考えていきます 44
おさらい 45 sync.Cond (条件変数) chan (チャネル) 関心事 共有されている「状態」が変化したこと。 「データ」そのものを、あるゴルーチンから別のゴ ルーチンへ送ること。
役割 状態の変化を待っているゴルーチン全員に 「状態が変わったかもしれないから、確認してくだ さい」と知らせる。 データを特定のゴルーチンに 直接、安全に届ける。 その後 起こされたゴルーチンたちは、ロックを 再取得できたものから一つずつ処理をする データを受け取ったゴルーチンは、 そのデータに対する所有権を得て、 他のゴルーチンと競合することなく 処理を開始できる
なぜDeltaFIFOはsync.Condを選んだのか? そもそもKubernetesのリソース変更検知の仕組みは、複数のリソースが監視しています。 例えば、、、 「Podが作られた」というイベントひとつで以下のようなことが非同期に起こります →ネットワーク設定をするリソースに通知され、登録される →Podの総数を監視しているリソースに通知され、古い Podが削除される(こともある) →サービスメッシュのための仕組みを管理するリソースに通知され、コンテナが注入される(こともある) などなど 46
なぜDeltaFIFOはsync.Condを選んだのか? つまり、ひとつの変化しうる状態を n個のリソースが監視している というシチュエーションで使われるわけです 47
なぜDeltaFIFOはsync.Condを選んだのか? Goで実装されているのでGoに置き換えると 一つの「変化しうる状態」を複数のゴルーチンで監視している状況です。 複数のゴルーチンがアクセスするため、 データの整合性を守るsync.Mutexは、必須です。 48
なぜDeltaFIFOはsync.Condを選んだのか? 「ロックで守られた、たった一つのデータを複数のゴルーチンで共有している」 と言い換えることができます。 49
なぜDeltaFIFOはsync.Condを選んだのか? sync.CondはすでにあるMutexに後付けで 「待機/通知」の機能を追加するために設計されていると考えることができます。 DeltaFIFOは必須であるMutexにsync.Condを組み合わせるだけで、 効率的な待機メカニズムを最小限のオーバーヘッドで実装できます。 50
なぜDeltaFIFOはsync.Condを選んだのか? では、この設計でチャネルを使うとどうなるでしょう? 結局、キューとマップを守るための Mutexは必要です。 その上で、Popする側に「アイテムが追加されたこと」を知らせるためだけの 通知用チャネルを別途用意することになります。 その結果として、、、 51
なぜDeltaFIFOはsync.Condを選んだのか? DeltaFIFOのMutexと、チャネル内部のMutexという、 二重のロックが発生することになります データそのものではなく、ただの合図を送るためだけにチャネルを使うことになり、 不自然な設計になる可能性が高いです。 だからチャネルではなくsync.Condを選んだと考えられます。 52
まとめ • チャネルとsync.Condには明確な役割の違いがある • チャネルはデータの流れを作るのに適していて、 sync.Condは共有された状態を中心にゴルーチンを待機させるのに適している • k8s.io/client-go cache.DeltaFIFOはちゃんと理由があってsync.Condを選択した •
その理由は、Mutexで保護された単一の共有キューという設計に、 sync.Condが極めて自然に、かつ効率的に統合できたから 53
おわり
参考文献 • k8s.io/client-go • Go1.25.1のソースコード • Goでの並行処理を徹底解剖! - Zenn •
Goの並行処理入門 - Goroutine基礎編 • Goの並行処理入門 - syncパッケージ編 55