package mirror

import (
    "github.com/coreos/etcd/clientv3"
    "golang.org/x/net/context"
)

const (
    batchLimit = 1000
)

// Syncer syncs with the key-value state of an etcd cluster.
type Syncer interface {
    // SyncBase syncs the base state of the key-value state.
    // The key-value state are sent through the returned chan.
    SyncBase(ctx context.Context) (<-chan clientv3.GetResponse, chan error)
    // SyncUpdates syncs the updates of the key-value state.
    // The update events are sent through the returned chan.
    SyncUpdates(ctx context.Context) clientv3.WatchChan
}

// NewSyncer creates a Syncer.
func NewSyncer(c *clientv3.Client, prefix string, rev int64) Syncer {
    return &syncer{c: c, prefix: prefix, rev: rev}
}

type syncer struct {
    c      *clientv3.Client
    rev    int64
    prefix string
}

func (s *syncer) SyncBase(ctx context.Context) (<-chan clientv3.GetResponse, chan error) {
    respchan := make(chan clientv3.GetResponse, 1024)
    errchan := make(chan error, 1)

    // if rev is not specified, we will choose the most recent revision.
    if s.rev == 0 {
        resp, err := s.c.Get(ctx, "foo")
        if err != nil {
            errchan <- err
            close(respchan)
            close(errchan)
            return respchan, errchan
        }
        s.rev = resp.Header.Revision
    }

    go func() {
        defer close(respchan)
        defer close(errchan)

        var key string

        opts := []clientv3.OpOption{clientv3.WithLimit(batchLimit), clientv3.WithRev(s.rev)}

        if len(s.prefix) == 0 {
            // If len(s.prefix) == 0, we will sync the entire key-value space.
            // We then range from the smallest key (0x00) to the end.
            opts = append(opts, clientv3.WithFromKey())
            key = "\x00"
        } else {
            // If len(s.prefix) != 0, we will sync key-value space with given prefix.
            // We then range from the prefix to the next prefix if exists. Or we will
            // range from the prefix to the end if the next prefix does not exists.
            opts = append(opts, clientv3.WithRange(clientv3.GetPrefixRangeEnd(s.prefix)))
            key = s.prefix
        }

        for {
            resp, err := s.c.Get(ctx, key, opts...)
            if err != nil {
                errchan <- err
                return
            }

            respchan <- (clientv3.GetResponse)(*resp)

            if !resp.More {
                return
            }
            // move to next key
            key = string(append(resp.Kvs[len(resp.Kvs)-1].Key, 0))
        }
    }()

    return respchan, errchan
}

func (s *syncer) SyncUpdates(ctx context.Context) clientv3.WatchChan {
    if s.rev == 0 {
        panic("unexpected revision = 0. Calling SyncUpdates before SyncBase finishes?")
    }
    return s.c.Watch(ctx, s.prefix, clientv3.WithPrefix(), clientv3.WithRev(s.rev+1))
}

syncer.go的更多相关文章

  1. TiDB数据库 使用syncer工具同步实时数据

    mysql> select campaign_id ,count(id) from creative_output group by campaign_id; rows min 44.23 se ...

  2. C#中级-常用多线程操作(持续更新)

    一.前言       多线程操作一直是编程的常用操作,掌握好基本的操作可以让程序运行的更加有效.本文不求大而全,只是将我自己工作中常常用到的多线程操作做个分类和总结.平时记性不好的时候还能看看.本文参 ...

  3. Heartbeat+DRBD+MySQL高可用方案

    1.方案简介 本方案采用Heartbeat双机热备软件来保证数据库的高稳定性和连续性,数据的一致性由DRBD这个工具来保证.默认情况下只有一台mysql在工作,当主mysql服务器出现问题后,系统将自 ...

  4. drdb

    Distributed Replicated Block Device(DRBD)是一种基于软件的,无共享,复制的存储解决方案,在服务器之间的对块设备(硬盘,分区,逻辑卷等)进行镜像.DRBD工作在内 ...

  5. Linux 集群

    html,body { } .CodeMirror { height: auto } .CodeMirror-scroll { } .CodeMirror-lines { padding: 4px 0 ...

  6. ZABBIX冗余架构构筑(Centos6.4+pacemaker+corosync+drbd)

    基本构成: 用pacemaker+corosync控制心跳和资源迁移 用drbd同步zabbix配置文件和mysql数据库 所有软件都用yum安装至默认路径 主机的drbd领域挂载至/drbd,备机不 ...

  7. MySQL 系列(五) 多实例、高可用生产环境实战

    MySQL 系列(五) 多实例.高可用生产环境实战   第一篇:MySQL 系列(一) 生产标准线上环境安装配置案例及棘手问题解决 第二篇:MySQL 系列(二) 史上最屌.你不知道的数据库操作 第三 ...

  8. 1 构建Mysql+heartbeat+DRBD+LVS集群应用系统系列之DRBD的搭建

    preface 近来公司利润上升,购买了10几台服务器,趁此机会,把mysql的主从同步的架构进一步扩展,为了适应日益增长的流量.针对mysql架构的扩展,先是咨询前辈,后和同事探讨,准备采用Mysq ...

  9. linux 后台运行命令 nohup命令

    转载:http://if.ustc.edu.cn/~ygwu/blog/archives/000538.html 2005年04月18日 简单而有用的nohup命令在UNIX/LINUX中,普通进程用 ...

随机推荐

  1. 深入浅出理解python 装饰器

    之前就了解到了装饰器, 但是就会点皮毛, 而且对其调用方式感到迷茫,正好现在的项目我想优化,就想到了用装饰器, 因此深入研究了下装饰器.先看下代码: import time # 将函数作为参数传入到此 ...

  2. 万水千山ABP - 系统发布后迁移 CodeFirst 数据库[原创]

    在项目开发的过程中,常会遇到项目发布后还变更数据库的情况.这时如何方便地进行数据库迁移呢 ? 下面直接列出操作的步骤: 1. 发布修改后的应用: 将最新版本的应用更新到目标机器中.更新的文件当然不包括 ...

  3. 你必须知道的261个Java语言问题

    1. Java语言的运行机制: Java既不是编译型语言也不是解释型语言,它是编译型和解释型语言的结合体.首先采用通用的java编译器将Java源程序编译成为与平台无关的字节码文件(class文件), ...

  4. Android Studio集成Lombok Plugin

    Lombok是Android Studio名列前茅的插件,有啥用,看这个:http://www.blogjava.NET/fancydeepin/archive/2012/07/12/lombok.h ...

  5. Interesting卡常数

    C++ Interesting卡常数 作为一名OIer,在Noip中卡(kǎ 我就爱读kǎ)常数可以说是必备技巧.在此总结一下我所知卡常数的神奇手法: IO优化 fread 和 fwrite ,如果还 ...

  6. JSF-使用JSF标记

    使用JSF标记 基于Facelets技术的JSF页面是一个 XHTML页面,文件扩展名为 .xhtml 1)JSF页面可用html标记,但必须满足: ①所有标记都必须闭合.如<p>开始,& ...

  7. Oracle12c中数据泵新特性之功能增强(expdp, impdp)

    Oracle的数据泵功能在10g中被引进.本文对数据泵在12c中的增强做一个概览. 1.   禁用日志选项(DISABLE_ARCHIVE_LOGGING) Impdp的TRANSFORM参数已经扩展 ...

  8. Spark学习笔记

    Map-Reduce 我认为上图代表着MapReduce不仅仅包括Map和Reduce两个步骤这么简单,还有两个隐含步骤没有明确,全部步骤包括:切片.转换.聚合.叠加,按照实际的运算场景上述步骤可以简 ...

  9. longestCommonPrefix

    Description: Write a function to find the longest common prefix string amongst an array of strings. ...

  10. 拾人牙慧篇之——基于HTML5中websocket来实现消息推送功能

    一.写在前面 要求做一个,后台发布信息,前台能即时得到通知的消息推送功能.网上搜了也有很多方式,ajax的定时询问,Comet方式,Server-Sent方式,以及websocket.表示除了定时询问 ...