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
9
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
プラットフォーム転換期におけるGitHub Copilot活用〜Coding agentがそれを加速するか〜 / Leveraging GitHub Copilot During Platform Transition Periods
aeonpeople
1
230
S3アクセス制御の設計ポイント
tommy0124
3
200
[ JAWS-UG 東京 CommunityBuilders Night #2 ]SlackとAmazon Q Developerで 運用効率化を模索する
sh_fk2
3
460
RSCの時代にReactとフレームワークの境界を探る
uhyo
10
3.5k
下手な強制、ダメ!絶対! 「ガードレール」を「檻」にさせない"ガバナンス"の取り方とは?
tsukaman
2
460
2025/09/16 仕様駆動開発とAI-DLCが導くAI駆動開発の新フェーズ
masahiro_okamura
0
110
LLM時代のパフォーマンスチューニング:MongoDB運用で試したコンテキスト活用の工夫
ishikawa_pro
0
170
Autonomous Database - Dedicated 技術詳細 / adb-d_technical_detail_jp
oracle4engineer
PRO
4
10k
いま注目のAIエージェントを作ってみよう
supermarimobros
0
350
今日から始めるAWSセキュリティ対策 3ステップでわかる実践ガイド
yoshidatakeshi1994
0
110
Codeful Serverless / 一人運用でもやり抜く力
_kensh
7
450
スマートファクトリーの第一歩 〜AWSマネージドサービスで 実現する予知保全と生成AI活用まで
ganota
2
310
Featured
See All Featured
The Language of Interfaces
destraynor
161
25k
Building Adaptive Systems
keathley
43
2.7k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
29
1.9k
How to Ace a Technical Interview
jacobian
279
23k
Build The Right Thing And Hit Your Dates
maggiecrowley
37
2.9k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
4k
Optimising Largest Contentful Paint
csswizardry
37
3.4k
GraphQLの誤解/rethinking-graphql
sonatard
72
11k
Bash Introduction
62gerente
615
210k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
229
22k
Keith and Marios Guide to Fast Websites
keithpitt
411
22k
Learning to Love Humans: Emotional Interface Design
aarron
273
40k
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