1. 每个文件(无论是archived data的file还是wal)都有一个index, 这需要应用来维护(vnode里,该index就是fileId*3 + 0/1/2, 对应data, head与last三个文件)。如果index为0,表示系统里最老的数据文件。对于mode里的文件,数量是固定的,对应于acct, user, db, table等文件。
    2. 任何一个数据文件(file)有名字、大小,还有一个magic number。只有文件名、大小与magic number一致时,两个文件才判断是一样的,无需同步。Magic number可以是checksum, 也可以是简单的文件大小。怎么计算magic,换句话说,如何检测数据文件是否有效,完全由应用决定。
    3. 文件名的处理有点复杂,因为每台服务器的路径可能不一致。比如node A的TDengine的数据文件存放在 /etc/taos目录下,而node B的数据存放在 /home/jhtao目录下。因此同步模块需要应用在启动一个同步实例时提供一个path,这样两台服务器的绝对路径可以不一样,但仍然可以做对比,做同步。
    4. 当sync模块调用回调函数getFileInfo获得数据文件信息时,有如下的规则
      • index 为0,表示获取最老的文件,同时修改index返回给sync模块。如果index不为0,表示获取指定位置的文件。
      • 如果name为空,表示sync想获取位于index位置的文件信息,包括magic, size。Master节点会这么调用
      • 如果name不为空,表示sync想获取指定文件名和index的信息,slave节点会这么调用
      • 如果某个index的文件不存在,magic返回0,表示文件已经是最后一个。因此整个系统里,文件的index必须是连续的一段整数。
    5. 当sync模块调用回调函数getWalInfo获得wal信息时,有如下规则
      • index为0,表示获得最老的WAL文件, 返回时,index更新为具体的数字
      • 返回的文件名为空,那表示没有WAL文件
    6. 无论是getFileInfo, 还是getWalInfo, 只要获取出错(不是文件不存在),返回-1即可,系统会报错,停止同步

    整个数据恢复流程分为两大步骤,第一步,先恢复archived data(file), 然后恢复wal。具体流程如下:

    1. 通过已经建立的TCP连接,发送sync req给master节点
    2. master收到sync req后,以client的身份,向vnode B主动建立一新的专用于同步的TCP连接(syncFd)
    3. 新的TCP连接建立成功后,master将开始retrieve流程,对应的,vnode B将同步启动restore流程
    4. Retrieve/Restore流程里,先处理所有archived data (vnode里的data, head, last文件),后处理WAL data。
    5. 对于archived data,master将通过回调函数getFileInfo获取数据文件的基本信息,包括文件名、magic以及文件大小。
    6. master 将获得的文件名、magic以及文件大小发给vnode B
    7. vnode B将回调函数getFile获得magic和文件大小,如果两者一致,就认为无需同步,如果两者不一致 ,就认为需要同步。vnode B将结果通过消息FileAck发回master
    8. 如果文件需要同步,master就调用sendfile把整个文件发往vnode B

    对于WAL同步,流程如下:

    1. master节点调用回调函数getWalInfo,获取WAL的文件名。
    2. 如果getWalInfo返回值大于0,表示该文件还不是最后一个WAL,因此master调用sendfile一下把该文件发送给vnode B
    3. 如果getWalInfo返回时为0,表示该文件是最后一个WAL,因为文件可能还处于写的状态中,sync模块要根据WAL Head的定义逐条读出记录,然后发往vnode B。
    4. vnode A读取TCP连接传来的数据,按照WAL Head,逐条读取,如果版本号比现有的大,调用回调函数writeToCache,交给应用处理。如果小,直接扔掉。
    5. 上述流程循环,直到所有WAL文件都被处理完。处理完后,master就会将新来的数据包通过Forward消息转发给slave。

    对于最后一个WAL (LastWal)的处理逻辑有点复杂,因为这个文件往往是打开写的状态,有很多场景需要考虑,比如:

    • LastWal文件size在增长,需要重新读;
    • LastWal文件虽然已经打开写,但内容为空;
    • LastWal文件已经被关闭,应用生成了新的Last WAL文件;
    • LastWal文件没有被关闭,但数据落盘的原因,没有读到完整的一条记录;
    • LastWal文件没有被关闭,但数据落盘的原因,还有部分记录暂时读取不到;

    等vnode B把这些buffered forwards处理完,同步流程才算结束,vnode B正式变为slave。