예제 #1
0
파일: replication.go 프로젝트: eswdd/bosun
func (app *App) slaveof(masterAddr string, restart bool, readonly bool) error {
	app.m.Lock()
	defer app.m.Unlock()

	//in master mode and no slaveof, only set readonly
	if len(app.cfg.SlaveOf) == 0 && len(masterAddr) == 0 {
		app.cfg.SetReadonly(readonly)
		return nil
	}

	if !app.ldb.ReplicationUsed() {
		return fmt.Errorf("slaveof must enable replication")
	}

	app.cfg.SlaveOf = masterAddr

	if len(masterAddr) == 0 {
		log.Infof("slaveof no one, stop replication")
		if err := app.m.stopReplication(); err != nil {
			return err
		}

		app.cfg.SetReadonly(readonly)
	} else {
		return app.m.startReplication(masterAddr, restart)
	}

	return nil
}
예제 #2
0
파일: replication.go 프로젝트: eswdd/bosun
func (app *App) removeSlave(c *client, activeQuit bool) {
	addr := c.slaveListeningAddr

	app.slock.Lock()
	defer app.slock.Unlock()

	if _, ok := app.slaves[addr]; ok {
		delete(app.slaves, addr)
		log.Infof("remove slave %s", addr)
		asyncNotifyUint64(app.slaveSyncAck, c.lastLogID.Get())
	}
}
예제 #3
0
파일: replication.go 프로젝트: eswdd/bosun
func (app *App) publishNewLog(l *rpl.Log) {
	if !app.cfg.Replication.Sync {
		//no sync replication, we will do async
		return
	}

	app.info.Replication.PubLogNum.Add(1)

	app.slock.Lock()

	slaveNum := len(app.slaves)

	total := (slaveNum + 1) / 2
	if app.cfg.Replication.WaitMaxSlaveAcks > 0 {
		total = num.MinInt(total, app.cfg.Replication.WaitMaxSlaveAcks)
	}

	n := 0
	logId := l.ID
	for _, s := range app.slaves {
		lastLogID := s.lastLogID.Get()
		if lastLogID == logId {
			//slave has already owned this log
			n++
		} else if lastLogID > logId {
			log.Errorf("invalid slave %s, lastlogid %d > %d", s.slaveListeningAddr, lastLogID, logId)
		}
	}

	app.slock.Unlock()

	if n >= total {
		//at least total slaves have owned this log
		return
	}

	startTime := time.Now()
	done := make(chan struct{}, 1)
	go func() {
		n := 0
		for i := 0; i < slaveNum; i++ {
			id := <-app.slaveSyncAck
			if id < logId {
				log.Infof("some slave may close with last logid %d < %d", id, logId)
			} else {
				n++
				if n >= total {
					break
				}
			}
		}
		done <- struct{}{}
	}()

	select {
	case <-done:
	case <-time.After(time.Duration(app.cfg.Replication.WaitSyncTime) * time.Millisecond):
		log.Info("replication wait timeout")
	}

	stopTime := time.Now()
	app.info.Replication.PubLogAckNum.Add(1)
	app.info.Replication.PubLogTotalAckTime.Add(stopTime.Sub(startTime))
}
예제 #4
0
파일: cmd_migrate.go 프로젝트: eswdd/bosun
//XMIGRATE host port type key destination-db timeout
//will block any other write operations
//maybe only for xcodis
func xmigrateCommand(c *client) error {
	args := c.args

	if len(args) != 6 {
		return ErrCmdParams
	}

	addr := fmt.Sprintf("%s:%s", string(args[0]), string(args[1]))
	if addr == c.app.cfg.Addr {
		//same server, can not migrate
		return fmt.Errorf("migrate in same server is not allowed")
	}

	tp := strings.ToUpper(string(args[2]))
	key := args[3]
	db, err := parseMigrateDB(c, args[4])
	if err != nil {
		return err
	}

	timeout, err := ledis.StrInt64(args[5], nil)
	if err != nil {
		return err
	} else if timeout < 0 {
		return fmt.Errorf("invalid timeout %d", timeout)
	}

	conn, err := getMigrateDBConn(c, addr, db)
	if err != nil {
		return err
	}
	defer conn.Close()

	// if key is in migrating, we will wait 500ms and retry again
	for i := 0; i < 10; i++ {
		if tp == "ALL" {
			// if tp is ALL, we will migrate the key in all types
			// this feature is useful for xcodis RESTORE or other commands that we don't know the data type exactly
			err = migrateAllTypeKeys(c, conn, key, timeout)
		} else {
			err = migrateKey(c, conn, tp, key, timeout)
		}

		if err != errKeyInMigrating {
			break
		} else {
			log.Infof("%s key %s is in migrating, wait 500ms and retry", tp, key)
			time.Sleep(500 * time.Millisecond)
		}
	}

	if err != nil {
		if err == errNoKey {
			c.resp.writeStatus(NOKEY)
			return nil
		} else {
			return err
		}
	}

	c.resp.writeStatus(OK)
	return nil
}