예제 #1
0
// NewDBConnection returns a new DBConnection based on the ConnectionParams
// and will use the provided stats to collect timing.
func NewDBConnection(info *mysql.ConnectionParams, mysqlStats *stats.Timings) (*DBConnection, error) {
	params, err := dbconfigs.MysqlParams(info)
	if err != nil {
		return nil, err
	}
	c, err := mysql.Connect(params)
	return &DBConnection{c, mysqlStats}, err
}
예제 #2
0
func (dc *DBClient) Connect() error {
	params, err := dbconfigs.MysqlParams(dc.dbConfig)
	if err != nil {
		return err
	}
	dc.dbConn, err = mysql.Connect(params)
	if err != nil {
		return fmt.Errorf("error in connecting to mysql db, err %v", err)
	}
	return nil
}
예제 #3
0
// NewSlaveConnection creates a new slave connection to the mysqld instance.
// It uses a pools.IDPool to ensure that the server IDs used to connect are
// unique within this process. This is done with the assumptions that:
//
// 1) No other processes are making fake slave connections to our mysqld.
// 2) No real slave servers will have IDs in the range 1-N where N is the peak
//    number of concurrent fake slave connections we will ever make.
func NewSlaveConnection(mysqld *Mysqld) (*SlaveConnection, error) {
	params, err := dbconfigs.MysqlParams(mysqld.dba)
	if err != nil {
		return nil, err
	}

	conn, err := mysql.Connect(params)
	if err != nil {
		return nil, err
	}

	sc := &SlaveConnection{
		Connection: conn,
		mysqld:     mysqld,
		slaveID:    slaveIDPool.Get(),
	}
	log.Infof("new slave connection: slaveID=%d", sc.slaveID)
	return sc, nil
}
예제 #4
0
// setupPrimerConnections creates the channel and the consumers for
// the sql statements
func (pc *PrimeCache) setupPrimerConnections() error {
	pc.workerChannel = make(chan string, 1000)
	for i := 0; i < pc.WorkerCount; i++ {
		// connect to the database using client for a replay connection
		params, err := dbconfigs.MysqlParams(&pc.dbcfgs.App.ConnectionParams)
		if err != nil {
			return fmt.Errorf("cannot get parameters to connect to MySQL: %v", err)
		}

		dbConn, err := mysql.Connect(params)
		if err != nil {
			return fmt.Errorf("mysql.Connect failed: %v", err)
		}

		// and launch the go routine that applies the statements
		go applyLoop(dbConn, pc.workerChannel)
	}
	return nil
}
예제 #5
0
// OneRun tries a single cycle connecting to MySQL, and if behind on
// replication, starts playing the logs ahead to prime the cache.
func (pc *PrimeCache) OneRun() {
	// connect to the database using dba for a control connection
	params, err := dbconfigs.MysqlParams(&pc.dbcfgs.Dba)
	if err != nil {
		log.Errorf("Cannot get parameters to connect to MySQL: %v", err)
		return
	}

	pc.dbConn, err = mysql.Connect(params)
	if err != nil {
		log.Errorf("mysql.Connect failed: %v", err)
		return
	}

	// get the slave status
	slavestat, err := pc.getSlaveStatus()
	if err != nil {
		log.Warningf("getSlaveStatus failed: %v", err)
		return
	}

	// if we're not replicating, we're done
	if !slavestat.slaveSQLRunning {
		log.Warningf("Slave is not replicating (SQL)")
		return
	}
	if !slavestat.slaveIORunning {
		log.Warningf("Slave is not replicating (IO)")
		return
	}
	if slavestat.secondsBehindMaster < 2 {
		return
	}
	log.Infof("Replication lag is high (%v seconds), activating", slavestat.secondsBehindMaster)

	// setup the connections to the db to apply the statements
	if err := pc.setupPrimerConnections(); err != nil {
		log.Errorf("setupPrimerConnections failed: %v", err)
		return
	}

	// open the binlogs from where we are on
	reader, err := pc.openBinlog(slavestat)
	if err != nil {
		log.Errorf("openBinlog failed: %v", err)
		return
	}

	maxLineCount := 10000
	var maxLeadBytes int64 = 5000000

	// and start the loop
	lineCount := 0
	deleteCount := 0
	appliedDeleteCount := 0
	updateCount := 0
	appliedUpdateCount := 0
	sleepCount := 0
	var logPos uint64
	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		line := scanner.Text()
		lowerLine := strings.ToLower(line)
		lineCount++

		if p, isComment := parseLogPos(line); isComment {
			// handle the comments with a log pos
			if p > logPos {
				logPos = p
			}

		} else if s, isDelete := parseDeleteStatement(line, lowerLine); isDelete {
			// handle delete statements
			deleteCount++
			if s != "" {
				appliedDeleteCount++
				pc.workerChannel <- s
			}

		} else if s, isUpdate := parseUpdateStatement(line, lowerLine); isUpdate {
			// handle update statements
			updateCount++
			if s != "" {
				appliedUpdateCount++
				pc.workerChannel <- s
			}
		}

		if lineCount%maxLineCount == 0 {
			var leadBytes int64
			for {
				slavestat, err = pc.getSlaveStatus()
				if err != nil {
					log.Errorf("getSlaveStatus failed: %v", err)
					return
				}

				// see how far ahead we are (it's signed because
				// we can be behind too)
				leadBytes = int64(logPos) - int64(slavestat.execMasterLogPos)
				if leadBytes > maxLeadBytes {
					sleepCount++
					log.Infof("Sleeping for 1 second waiting for SQL thread to advance: %v > %v", leadBytes, maxLeadBytes)
					time.Sleep(1 * time.Second)
					continue
				} else {
					break
				}
			}

			log.Infof("STATS: readahead: %10d lag: %7d sleeps: %4d deletes: %10d missed updates: %10d updates: %10d\n",
				leadBytes, slavestat.secondsBehindMaster, sleepCount, deleteCount, updateCount-appliedUpdateCount, appliedUpdateCount)
		}
	}
	reader.Close()
	if err := scanner.Err(); err != nil {
		log.Errorf("Scanner failed: %v", err)
	}
}