<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Junyi's Lab</title><link>https://www.junyi.dev/</link><description>Recent blog posts on Junyi's Lab</description><generator>Hugo (https://gohugo.io)</generator><language>zh-cn</language><managingEditor>junyi.h@comp.nus.edu.sg (Junyi Hou)</managingEditor><webMaster>junyi.h@comp.nus.edu.sg (Junyi Hou)</webMaster><lastBuildDate>Tue, 12 Sep 2023 15:15:37 +0800</lastBuildDate><atom:link href="https://www.junyi.dev/tags/distributed-systems/index.xml" rel="self" type="application/rss+xml"/><item><title>Distributed System: Primary Backup Replication</title><link>https://www.junyi.dev/posts/paxos-review-3/</link><pubDate>Tue, 12 Sep 2023 15:15:37 +0800</pubDate><author>junyi.h@comp.nus.edu.sg (Junyi Hou)</author><description>
开胃菜来了！
你有一个硬盘，存了很多重要资料，你担心这个硬盘某天坏了，怎么办？
当然是多买点硬盘，把数据备份在不同的硬盘上。</description><content:encoded>&lt;p&gt;开胃菜来了！&lt;/p&gt;
&lt;p&gt;你有一个硬盘，存了很多重要资料，你担心这个硬盘某天坏了，怎么办？&lt;/p&gt;
&lt;p&gt;当然是多买点硬盘，把数据备份在不同的硬盘上。&lt;/p&gt;
&lt;p&gt;保证可靠，就是通过多个一致的数据副本来实现的。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="前提假设" &gt;
&lt;div&gt;
&lt;a href="#%e5%89%8d%e6%8f%90%e5%81%87%e8%ae%be"&gt;
#
&lt;/a&gt;
前提假设
&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;我们假设所有的操作都是&lt;ruby&gt;确定&lt;rt&gt;deterministic&lt;/rt&gt;&lt;/ruby&gt;的&lt;/p&gt;
&lt;!--
多线程那样，因为 scheduler 的原因导致每次执行的结果都不一样。 --&gt;
&lt;h2 id="最简单的-primary--backup" &gt;
&lt;div&gt;
&lt;a href="#%e6%9c%80%e7%ae%80%e5%8d%95%e7%9a%84-primary--backup"&gt;
#
&lt;/a&gt;
最简单的 primary &amp;amp; backup
&lt;/div&gt;
&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;client&lt;strong&gt;s&lt;/strong&gt; 发送 operation (Put, Get, Append) 到 primary-server&lt;/li&gt;
&lt;li&gt;primary-server 决定 operation 的执行顺序&lt;/li&gt;
&lt;li&gt;primary-server 把 operation 的执行顺序发送给 backup-server&lt;/li&gt;
&lt;li&gt;backup-server 按照 primary-server 发送的顺序执行 operation（hot-standby）
&lt;ul&gt;
&lt;li&gt;或者 backup-server 按照 primary-server 发送的顺序记录 operation 但不执行（cold-standby）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;等 backup-server 成功结束，primary-server 给 client 发回复&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;问题是，怎么决定谁来当 primary-server？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;怎么决定谁来当 backup-server？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;client 怎么知道谁是 primary-server？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;答案：&lt;/strong&gt; &lt;span class="spoiler"&gt;引入一台 view-server，让这个 view-server 来做决定，并且 client 去问 view-server 谁是 primary-server。为什么叫它 view-server？因为这涉及到一个重要概念「view」，后面会讲到，别着急！&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;知道了答案，我们可以这样设计整个系统：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;我们让每台服务器发送心跳给 view-server，告诉 view-server 自己还活着。&lt;/p&gt;
&lt;p&gt;问：有没有可能自己活着但是 view-server 认为自己死了？&lt;span class="spoiler"&gt; 有可能因为网络问题，心跳没发出去，view-server 认为节点挂了，但其实节点还活着&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;我们 promote idle-server 成为 backup-server，但不越级 promote 成为 primary-server&lt;/p&gt;
&lt;p&gt;问：除非有种情况？&lt;span class="spoiler"&gt;系统刚启动时（全都是 idle-server、需要一个 primary 的时候）&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;只有 backup-server 会被 promote 成 primary-server&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
&lt;img src="view-service.png" alt="View Service" width="500" /&gt;
&lt;figcaption&gt;Fig.1 - One View Server and Two Nodes&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="什么是-view-server-里的-view" &gt;
&lt;div&gt;
&lt;a href="#%e4%bb%80%e4%b9%88%e6%98%af-view-server-%e9%87%8c%e7%9a%84-view"&gt;
#
&lt;/a&gt;
什么是 view-server 里的 view
&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;一个 view 是一个 primary-server 和一个 backup-server 的集合，描述了当前状态&lt;strong&gt;谁是 primary 谁是 backup&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;注意，一个 view 里，只有一个 primary 和一个 backup，其它的 server 标记为 idle。（想想看为什么这样设计？）&lt;/p&gt;
&lt;p&gt;view 由 view-server 进行管理，其他节点可以获取到当前系统的 view。&lt;/p&gt;
&lt;figure&gt;
&lt;img src="view.png" alt="view-service" width="400" /&gt;
&lt;figcaption&gt;Fig.2 - Example of three different views, denotes three different states of the system&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;看了上面的描述，感觉自己又行了，于是我设计出了下面的流程&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;系统刚开始，挑一个 primary-server，然后挑一个 backup-server，其它的都是 idle-server，然后开始工作。&lt;/p&gt;
&lt;p&gt;primary 立马让 backup 成为新的 primary，从 idle 里面挑一个成为 backup，然后让这个新的 primary 把状态都发送给 backup&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;很简单嘛！这能有什么问题？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;话音刚落，问题就来了。&lt;/p&gt;
&lt;p&gt;请看下面这个情况：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#5af78e"&gt;`View 1, primary=A, backup=B;`&lt;/span&gt; 这时候 A 挂了，B 成为 primary，从 idle 里面挑一个 C 成为 backup，然后 B 发送状态给 C。
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#5af78e"&gt;`View 2, primary=B, backup=C;`&lt;/span&gt; B 发状态的时候挂了，C 成为 primary，idle 里面没机器了
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#5af78e"&gt;`View 3, primary=C, backup=_;`&lt;/span&gt; C：😅？（此时 C 根本不知道系统的状态是什么）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;所以我们刚刚对整个系统的描述，是有缺陷的。&lt;/p&gt;
&lt;p&gt;怎么改呢，我们让 view-server 必须等待 primary-server &lt;ruby&gt;确认&lt;rt&gt;ACK&lt;/rt&gt;&lt;/ruby&gt; 当前的 view。&lt;/p&gt;
&lt;p&gt;就算 primary-server 挂了，view-server 也要等 ACK。&lt;/p&gt;
&lt;p&gt;（直觉告诉我们，这种等待 ACK 的行为可能导致系统卡住）&lt;/p&gt;
&lt;h2 id="总结一下所有的规则" &gt;
&lt;div&gt;
&lt;a href="#%e6%80%bb%e7%bb%93%e4%b8%80%e4%b8%8b%e6%89%80%e6%9c%89%e7%9a%84%e8%a7%84%e5%88%99"&gt;
#
&lt;/a&gt;
总结一下所有的规则
&lt;/div&gt;
&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;在 view i+1 的 primary 必须是 view i 的 backup&lt;/p&gt;
&lt;p&gt;如果没有这个规则会怎样？&lt;span class="spoiler"&gt;&lt;strong&gt;Split brain:&lt;/strong&gt; primary A, backup B, but can&amp;rsquo;t reach view-server. C,D are promoted to primary and backup, C doesn&amp;rsquo;t know previous state.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;primary 必须等 backup 执行完，才可以回复 client&lt;/p&gt;
&lt;p&gt;如果没有这个规则会怎样？&lt;span class="spoiler"&gt;&lt;strong&gt;Missing write:&lt;/strong&gt; client writes to A，A crashes before writing to B, clients read from B&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;一定要转发 read() 给 backup 吗？（这是一个常见的优化操作）&lt;span class="spoiler"&gt;必须转发。不然可能出现 &lt;strong&gt;Stale read&lt;/strong&gt;: at &lt;em&gt;view 1&lt;/em&gt;, A,B are primary and backup, but A cannot reach view-server. now &lt;em&gt;view 2&lt;/em&gt;, B,C are primary backup. client 1 writes to B, client 2 reads from A. A returns outdated data（不过 stale read 依然符合 sequential consistency）&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果 view 是正确的，backup 必须接受&lt;ruby&gt;转发的请求&lt;rt&gt;Forwarded Request&lt;/rt&gt;&lt;/ruby&gt;，并且执行&lt;/p&gt;
&lt;p&gt;如果没有这个规则会怎样？&lt;span class="spoiler"&gt;&lt;strong&gt;Partially Split Brain&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;non-primary 节点&lt;strong&gt;必须拒绝&lt;/strong&gt; client 的请求&lt;/p&gt;
&lt;p&gt;如果没有这个规则会怎样？&lt;span class="spoiler"&gt;&lt;strong&gt;Inconsistencies:&lt;/strong&gt; client&amp;rsquo;s view may outdated, send request to old primary server.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;state-transfer 的时候不能有操作。（Atomic State Transfer）&lt;/p&gt;
&lt;p&gt;如果没有这个规则会怎样？&lt;span class="spoiler"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="脑裂-split-brain" &gt;
&lt;div&gt;
&lt;a href="#%e8%84%91%e8%a3%82-split-brain"&gt;
#
&lt;/a&gt;
脑裂 (Split-brain)
&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;这个词有点吓人。。。其实是在说：&lt;strong&gt;在网络故障时，两个及以上的节点都认为自己是 Leader (primary-server)。&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;View 1, primary=A, backup=B; 这时候 A 网络出故障无法连接到 view-server，B 成为新的 primary。
View 2, primary=B, backup=_; B 是 新的 primary，但 A 也认为自己是 primary&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在我们之前的规定里，只有 primary-server 会回复 client，但是脑裂的时候，两个节点都会回复 client，这就可能导致 client 收到的数据不一致。
（破坏了 Linearzability）&lt;/p&gt;
&lt;h2 id="系统卡住" &gt;
&lt;div&gt;
&lt;a href="#%e7%b3%bb%e7%bb%9f%e5%8d%a1%e4%bd%8f"&gt;
#
&lt;/a&gt;
系统卡住
&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;有哪些可能导致系统卡住（处理不了 client 的请求）的情况？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;view-server 挂了&lt;/li&gt;
&lt;li&gt;整个网络挂了&lt;/li&gt;
&lt;li&gt;client 只能练到 view-server，不能连接到 primary-server&lt;/li&gt;
&lt;li&gt;backup server 没有了（因为 primary 转移状态给 backup，转移完了回复 view-server 一个 ACK）&lt;/li&gt;
&lt;li&gt;状态转移之前 primary 挂了&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="重复写入" &gt;
&lt;div&gt;
&lt;a href="#%e9%87%8d%e5%a4%8d%e5%86%99%e5%85%a5"&gt;
#
&lt;/a&gt;
重复写入
&lt;/div&gt;
&lt;/h2&gt;
&lt;h2 id="为什么-primary-backup-比较难" &gt;
&lt;div&gt;
&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88-primary-backup-%e6%af%94%e8%be%83%e9%9a%be"&gt;
#
&lt;/a&gt;
为什么 Primary Backup 比较难
&lt;/div&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;primary 可能挂掉&lt;/li&gt;
&lt;li&gt;backup 可能挂掉&lt;/li&gt;
&lt;li&gt;通讯可能 临时/永久 挂掉&lt;/li&gt;
&lt;li&gt;参与者的决策可能存在延迟：
&lt;ul&gt;
&lt;li&gt;view server 不知道 primary 挂了&lt;/li&gt;
&lt;li&gt;primary 挂了吗？挂了之后恢复，还需要回复 client 吗？&lt;/li&gt;
&lt;li&gt;backup 挂了吗？state transfer 结束了吗？&lt;/li&gt;
&lt;li&gt;client 不知道 view 有没有切换&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="总结一下-view-server-的缺点" &gt;
&lt;div&gt;
&lt;a href="#%e6%80%bb%e7%bb%93%e4%b8%80%e4%b8%8b-view-server-%e7%9a%84%e7%bc%ba%e7%82%b9"&gt;
#
&lt;/a&gt;
总结一下 view-server 的缺点
&lt;/div&gt;
&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;view-server 自己存在单点故障问题&lt;/li&gt;
&lt;li&gt;view-server 必须等待 primary 的 ack，即使 primary 可能挂了也要等&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="状态机复制state-machine-replication" &gt;
&lt;div&gt;
&lt;a href="#%e7%8a%b6%e6%80%81%e6%9c%ba%e5%a4%8d%e5%88%b6state-machine-replication"&gt;
#
&lt;/a&gt;
&lt;ruby&gt;状态机复制&lt;rt&gt;State Machine Replication&lt;/rt&gt;&lt;/ruby&gt;
&lt;/div&gt;
&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;状态机有一个特性：&lt;/strong&gt; 任何初始状态一样的状态机，如果执行的命令序列一样，则最终达到的状态也一样。&lt;/p&gt;
&lt;p&gt;如果将此特性应用在多参与者进行协商共识上，可以理解为系统中存在多个具有完全相同的状态机（参与者），这些状态机能最终保持一致的关键就是起始状态完全一致和执行命令序列完全一致。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;根据状态机的特性，要让多台机器的最终状态一致，只要确保它们的初始状态是一致的，并且接收到的操作指令序列也是一致的即可，无论这个操作指令是新增、修改、删除抑或是其他任何可能的程序行为，都可以理解为要将一连串的操作日志正确地广播给各个分布式节点。&lt;/p&gt;
&lt;p&gt;我们并不要求所有节点的每一条指令都是同时开始、同步完成的，只要求在此期间的内部状态不能被外部观察到，且当操作指令序列执行完毕时，所有节点的最终的状态是一致的，这种模型就被称为状态机复制（State Machine Replication）。&lt;/p&gt;
&lt;style&gt;
.spoiler {
color: black;
background-color: black;
transition: color 0.2s;
border-radius: 5px;
padding-right: 5px;
padding-left: 5px;
}
/* .spoiler::before {
content: "hover me to see the answer";
color: white;
background-color: black;
} */
.spoiler:hover {
visibility: visible;
color: white
}
/*
.spoiler:hover::before {
display: none;
} */
&lt;/style&gt;
&lt;h2 id="tips" &gt;
&lt;div&gt;
&lt;a href="#tips"&gt;
#
&lt;/a&gt;
Tips
&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;实现 Paxos 的注意事项可以参考 Junhui 的 Cheatsheet&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id="reference" &gt;
&lt;div&gt;
&lt;a href="#reference"&gt;
#
&lt;/a&gt;
Reference
&lt;/div&gt;
&lt;/h2&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a href="https://jzhu.xyz/posts/paxos" target="_blank" rel="noopener noreferrer"&gt;https://jzhu.xyz/posts/paxos&lt;/a&gt; (Cheatsheet of MultiPaxos Impl)&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded><category>Distributed Systems</category><category>Tech</category><guid isPermaLink="true">https://www.junyi.dev/posts/paxos-review-3/</guid></item><item><title>Distributed System: Consistency</title><link>https://www.junyi.dev/posts/paxos-review-2/</link><pubDate>Tue, 12 Sep 2023 14:06:01 +0800</pubDate><author>junyi.h@comp.nus.edu.sg (Junyi Hou)</author><description>
这篇文章主要是对分布式系统中的一致性Consistency进行区分</description><content:encoded>&lt;p&gt;这篇文章主要是对分布式系统中的&lt;ruby&gt;一致性&lt;rt&gt;Consistency&lt;/rt&gt;&lt;/ruby&gt;进行区分&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;ruby&gt;弱一致性&lt;rt&gt;Weak Consistency&lt;/rt&gt;&lt;/ruby&gt;&lt;/strong&gt;：允许系统的行为与单一系统的行为不一致。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;ruby&gt;强一致性&lt;rt&gt;Strong Consistency&lt;/rt&gt;&lt;/ruby&gt;&lt;/strong&gt;：整个系统表现得像是一个单一的、无并发的实体。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;下面的一致性，由弱到强&lt;/p&gt;
&lt;h2 id="最终一致性eventual-consistency" &gt;
&lt;div&gt;
&lt;a href="#%e6%9c%80%e7%bb%88%e4%b8%80%e8%87%b4%e6%80%a7eventual-consistency"&gt;
#
&lt;/a&gt;
&lt;ruby&gt;最终一致性&lt;rt&gt;Eventual Consistency&lt;/rt&gt;&lt;/ruby&gt;
&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;系统最终会达到一致的状态。&lt;/p&gt;
&lt;h2 id="因果一致性causal-consistency" &gt;
&lt;div&gt;
&lt;a href="#%e5%9b%a0%e6%9e%9c%e4%b8%80%e8%87%b4%e6%80%a7causal-consistency"&gt;
#
&lt;/a&gt;
&lt;ruby&gt;因果一致性&lt;rt&gt;Causal Consistency&lt;/rt&gt;&lt;/ruby&gt;
&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;比 Sequential Consistency 更弱。
如果操作 A 依赖于操作 B，那么操作 B 发生在操作 A 之前，但是如果操作 A 和操作 B 之间没有因果关系，那么操作 A 和操作 B 的顺序是不确定的。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;有因才有果，因在果之前&lt;/p&gt;
&lt;p&gt;先看见因，再看见果&lt;/p&gt;
&lt;p&gt;如果一个问题被回答，显然问题本身得先在那里，因为给出答案的人必须已经看到这个问题，我们认为在问题和答案之间存在因果依赖。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;比如微信朋友圈需要服务器之间采用因果一致性，来保证用户刷朋友圈时不会看到评论所对应的答复而看不到对应的评论。&lt;/p&gt;
&lt;img src="wechat-arch.png" alt="drawing" width="600" /&gt;
&lt;p&gt;延伸阅读：怎么生成唯一且递增的 ID？&lt;a href="https://www.infoq.cn/article/wechat-serial-number-generator-architecture/" target="_blank" rel="noopener noreferrer"&gt;微信序列号生成器架构设计及演变&lt;/a&gt;&lt;a href="https://segmentfault.com/a/1190000040964518" target="_blank" rel="noopener noreferrer"&gt;雪花算法&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="序列一致性sequential-consistency" &gt;
&lt;div&gt;
&lt;a href="#%e5%ba%8f%e5%88%97%e4%b8%80%e8%87%b4%e6%80%a7sequential-consistency"&gt;
#
&lt;/a&gt;
&lt;ruby&gt;序列一致性&lt;rt&gt;Sequential Consistency&lt;/rt&gt;&lt;/ruby&gt;
&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;不管系统怎么运行，得到的结果就好像把所有节点的所有操作按照某个 sequential order 排序后运行，但是在这个 sequential order 中，来自同一个节点的操作仍然保持着它们在节点中被指定的顺序。&lt;/p&gt;
&lt;p&gt;换句话说，物理意义上的时间，不能决定&lt;ruby&gt;操作&lt;rt&gt;operations&lt;/rt&gt;&lt;/ruby&gt;的执行顺序&lt;/p&gt;
&lt;p&gt;用作 Transaction 时我们称之为 Serializability&lt;/p&gt;
&lt;p&gt;常用在 银行、金融数据库、事务&lt;/p&gt;
&lt;h2 id="线性一致性linearizable-consistency" &gt;
&lt;div&gt;
&lt;a href="#%e7%ba%bf%e6%80%a7%e4%b8%80%e8%87%b4%e6%80%a7linearizable-consistency"&gt;
#
&lt;/a&gt;
&lt;ruby&gt;线性一致性&lt;rt&gt;Linearizable Consistency&lt;/rt&gt;&lt;/ruby&gt;
&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;也叫 Linearizability&lt;/p&gt;
&lt;p&gt;是【顺序一致性 + 按照物理时间顺序执行】。&lt;/p&gt;
&lt;p&gt;满足线性一致性的数据结构，表现得像是一个单一的、无并发的实体。&lt;span style="color: #cd7a32;"&gt;&lt;strong&gt;是最强的保证&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;比如 Google Spanner&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;介绍到这里，基本上对 Consistency 有了大致的了解，接下来可以看 Primary Backup Replication 了。&lt;/p&gt;
&lt;h2 id="lab-2" &gt;
&lt;div&gt;
&lt;a href="#lab-2"&gt;
#
&lt;/a&gt;
Lab 2
&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;实现 Primary-backup replication：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;client 发送请求给 primary-server&lt;/li&gt;
&lt;li&gt;由一个 中央 view-server 决定谁是 primary 谁是 backup&lt;/li&gt;
&lt;li&gt;primary 挂了，backup 顶上来&lt;/li&gt;
&lt;/ol&gt;
&lt;img src="pbr.png" alt="drawing" width="400"/&gt;</content:encoded><category>Distributed Systems</category><category>Tech</category><guid isPermaLink="true">https://www.junyi.dev/posts/paxos-review-2/</guid></item><item><title>Distributed System: Ordering of events</title><link>https://www.junyi.dev/posts/paxos-review-1/</link><pubDate>Tue, 12 Sep 2023 11:32:04 +0800</pubDate><author>junyi.h@comp.nus.edu.sg (Junyi Hou)</author><description>
上个学期的 CS5223 Distributed Systems ，Prof. Li 讲的蛮好，尽管实现了 Basic Paxos 和 Multi Paxos（多亏了 Junhui 的 cheatsheet1），但是耐不住时间的拷打，写的 Paxos 都快忘光了&amp;amp;hellip;
所以，赶紧开一篇博客把想法都记录下来，以便之后的复习。</description><content:encoded>&lt;p&gt;上个学期的 &lt;a href="https://nusmods.com/courses/CS5223/distributed-systems" target="_blank" rel="noopener noreferrer"&gt;CS5223 Distributed Systems&lt;/a&gt; ，Prof. Li 讲的蛮好，尽管实现了 Basic Paxos 和 Multi Paxos（多亏了 Junhui 的 cheatsheet&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;），但是耐不住时间的拷打，写的 Paxos 都快忘光了&amp;hellip;&lt;/p&gt;
&lt;!-- （而且没实现分布式事务） --&gt;
&lt;p&gt;所以，赶紧开一篇博客把想法都记录下来，以便&lt;strong&gt;之后的复习&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;首先简单过一下这些基础概念&lt;/p&gt;
&lt;h2 id="happens-before-relationship" &gt;
&lt;div&gt;
&lt;a href="#happens-before-relationship"&gt;
#
&lt;/a&gt;
Happens-before relationship
&lt;/div&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Within a process, A comes before B then A→B, it also means&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;B &lt;strong&gt;could&lt;/strong&gt; have been influenced by A&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If a → b, then b !→ a&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What about a!→b, does that mean b→a? &lt;span style="color: white"&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If a → b, and b → c, then a → c (transitivity)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;a !→ b and b !→ a, means a and b are &lt;strong&gt;concurrent&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;no one can tell whether a or b happened first!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="lamport-clock" &gt;
&lt;div&gt;
&lt;a href="#lamport-clock"&gt;
#
&lt;/a&gt;
Lamport clock
&lt;/div&gt;
&lt;/h2&gt;
&lt;img src="logical-clock.png" alt="drawing" width="400"/&gt;
&lt;p&gt;We can use clocks to implement a lock.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Our goals are:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Only one process can hold the lock at a time.&lt;/li&gt;
&lt;li&gt;Grant the lock &lt;strong&gt;in request order&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Requesting processes &lt;strong&gt;eventually&lt;/strong&gt; get the lock.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Our assumptions are:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;In order&lt;/strong&gt; point-to-point message delivery.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No failures&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Implementation:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Each message carries a timestamp $T_m$&lt;/li&gt;
&lt;li&gt;Three message types:
&lt;ul&gt;
&lt;li&gt;request&lt;/li&gt;
&lt;li&gt;release&lt;/li&gt;
&lt;li&gt;acknowledge&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Each node&amp;rsquo;s state:
&lt;ul&gt;
&lt;li&gt;A queue of requested messaged, ordered by $T_m$.&lt;/li&gt;
&lt;li&gt;The latest timestamp it received from each node.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;On receiving&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;receiving a request
&lt;ul&gt;
&lt;li&gt;record the timestamp&lt;/li&gt;
&lt;li&gt;add the request to the queue&lt;/li&gt;
&lt;li&gt;send an acknowledge&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;receiving a release
&lt;ul&gt;
&lt;li&gt;record the timestamp&lt;/li&gt;
&lt;li&gt;remove the request from the queue&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;receiving an acknowledge
&lt;ul&gt;
&lt;li&gt;record the timestamp&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;To perform&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;To acquire a lock
&lt;ul&gt;
&lt;li&gt;send request to every node, including itself&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;To release a lock
&lt;ul&gt;
&lt;li&gt;send release to every node, including itself&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;The lock is acquired when&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;My request is at the head of the queue.&lt;/li&gt;
&lt;li&gt;I&amp;rsquo;ve received higher timestamp &lt;strong&gt;from every node&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;So my request must be the earliest.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="vector-clock" &gt;
&lt;div&gt;
&lt;a href="#vector-clock"&gt;
#
&lt;/a&gt;
Vector clock
&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;Clock is a vector C, length = # of nodes.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;(0, 0, 0) for a 3-node system&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;On node i, increment C[i] on each event.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;node 0 (3,5,2), after event: (4,5,2)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;On node i, received a message, increment C[i], and take the max of each element.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;node 0 (4,5,2), received message (2,7,0): (4,7,2)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;If Cx[i] &amp;lt; Cy[i] and Cx[j] &amp;gt; Cy[j] for some i, j&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cx and Cy are concurrent&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;if Cx[i] &amp;lt;= Cy[i] for all i, and there exist j such that Cx[j] &amp;lt; Cy[j]&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cx happens before Cy&lt;/li&gt;
&lt;/ul&gt;
&lt;img src="vector-lock.jpg" width="400" alt="vector-lock" /&gt;
&lt;h2 id="lab-1" &gt;
&lt;div&gt;
&lt;a href="#lab-1"&gt;
#
&lt;/a&gt;
Lab 1
&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;实现一个 at-most-once 的 KV Store&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RPC 要么最多执行一次，要么不执行&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="reference" &gt;
&lt;div&gt;
&lt;a href="#reference"&gt;
#
&lt;/a&gt;
Reference
&lt;/div&gt;
&lt;/h2&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a href="https://jzhu.xyz/posts/paxos" target="_blank" rel="noopener noreferrer"&gt;https://jzhu.xyz/posts/paxos&lt;/a&gt; (Cheatsheet of MultiPaxos Impl)&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded><category>Distributed Systems</category><category>Tech</category><guid isPermaLink="true">https://www.junyi.dev/posts/paxos-review-1/</guid></item><item><title>&lt;ruby&gt;垂直联邦学习&lt;rt&gt;Vertical Federated Learning (VFL)&lt;/rt&gt;&lt;/ruby&gt;</title><link>https://www.junyi.dev/posts/vfl-1/</link><pubDate>Sat, 26 Aug 2023 19:32:24 +0800</pubDate><author>junyi.h@comp.nus.edu.sg (Junyi Hou)</author><description>
这是一个科普性质的文章
之前很抗拒做 VFL，因为我认为 VFL 只是一个训练模型的工作。
但是，我刚刚意识到 VFL 不只是深度学习。（一种突然开窍的感觉）</description><content:encoded>&lt;blockquote&gt;
&lt;p&gt;这是一个科普性质的文章&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;之前很抗拒做 VFL，因为我认为 VFL 只是一个训练模型的工作。&lt;/p&gt;
&lt;p&gt;但是，我刚刚意识到 VFL 不只是深度学习。（一种突然开窍的感觉）&lt;/p&gt;
&lt;p&gt;实际上，&lt;ruby&gt;垂直联邦学习&lt;rt&gt;Vertical Federated Learning&lt;/rt&gt;&lt;/ruby&gt; 在 &lt;ruby&gt;联邦学习&lt;rt&gt;Federated Learning&lt;/rt&gt;&lt;/ruby&gt; 领域里，研究的核心是 &lt;code&gt;Record Linkage&lt;/code&gt; / &lt;code&gt;Data Integration&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这玩意被深度学习包装一下就变成了 VFL。&lt;/p&gt;
&lt;p&gt;研究 HFL 的人比研究 VFL 的人多。因为 VFL 的数据太难获得了！&lt;/p&gt;
&lt;figure&gt;
&lt;div style="display: flex; align-items: end"&gt;
&lt;div style=""&gt;
&lt;img src="https://www.junyi.dev/posts/vfl-1/hfl.png" alt="Horizontal Federated Learning" /&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div style=""&gt;
&lt;img src="https://www.junyi.dev/posts/vfl-1/vfl.png" alt="Vertical Federated Learning"/&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;figcaption&gt;图 1 - HFL 和 VFL 的区别在于如何对待数据的 Column(Feature) &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;!-- &lt;figure&gt;
&lt;img width="250" src="https://www.junyi.dev/posts/vfl-1/procedure.png" alt="Procedure of Vertical Federated Learning"/&gt;
&lt;figcaption&gt;图 2 - VFL 的一般流程（SplitNN）&lt;/figcaption&gt;
&lt;/figure&gt; --&gt;
&lt;p&gt;想想看，什么样的场景需要用到 VFL 呢？&lt;/p&gt;
&lt;p&gt;比如银行和医院可以合作训练一个模型，预测用户的健康状态 / 还款能力。&lt;/p&gt;
&lt;p&gt;训练这样一个模型之前，我们应该先把银行和医院的数据对齐一下（做一下 Record Linkage）。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Record Linkage&lt;/strong&gt;: the process of identifying and linking records that correspond to the same individual across different databases.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure&gt;
&lt;img src="https://www.junyi.dev/posts/vfl-1/rl.png" alt="Vertical Federated Learning"/&gt;
&lt;figcaption&gt;图 2 - 经过 Record Linkage 过程，我们找到了银行和医院中的 Junyi Hou。&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img src="https://www.junyi.dev/posts/vfl-1/pprl.png" alt="Vertical Federated Learning"/&gt;
&lt;figcaption&gt;图 3 - 在 Privacy Preserving (保护隐私) 的情况下，Record Linkage 变得困难。 &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;在 Privacy Preserving 的情况下，如何做 Record Linkage 呢？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一个方法是 &lt;a href="https://github.com/Xtra-Computing/FedSim" target="_blank" rel="noopener noreferrer"&gt;FedSim (NeurIPS2022)&lt;/a&gt;，取 TopK 个最相关的 record 做 linkage &amp;hellip;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;VFL 数据不够的情况下，我们怎么广泛评估算法好坏呢？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;大部分研究者手动切分数据集，要么随机切，要么平均切，要么看心情切。&lt;/p&gt;
&lt;p&gt;比如数据集有 16 个 feature，我分成 2 个 party，partyA 有 9 个 feature，partyB 有 7 个 feature。&lt;/p&gt;
&lt;p&gt;直到 &lt;a href="https://vertibench.xtra.science/" target="_blank" rel="noopener noreferrer"&gt;VertiBench (ICLR2024)&lt;/a&gt; 这篇工作，我们才有一个切分数据集的&lt;strong&gt;指导纲领&lt;/strong&gt;（两个指标）。&lt;/p&gt;</content:encoded><category>Distributed Systems</category><category>Research</category><guid isPermaLink="true">https://www.junyi.dev/posts/vfl-1/</guid></item></channel></rss>