123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- package couchbase
- import (
- "fmt"
- "github.com/couchbase/goutils/logging"
- "sync"
- )
- type PersistTo uint8
- const (
- PersistNone = PersistTo(0x00)
- PersistMaster = PersistTo(0x01)
- PersistOne = PersistTo(0x02)
- PersistTwo = PersistTo(0x03)
- PersistThree = PersistTo(0x04)
- PersistFour = PersistTo(0x05)
- )
- type ObserveTo uint8
- const (
- ObserveNone = ObserveTo(0x00)
- ObserveReplicateOne = ObserveTo(0x01)
- ObserveReplicateTwo = ObserveTo(0x02)
- ObserveReplicateThree = ObserveTo(0x03)
- ObserveReplicateFour = ObserveTo(0x04)
- )
- type JobType uint8
- const (
- OBSERVE = JobType(0x00)
- PERSIST = JobType(0x01)
- )
- type ObservePersistJob struct {
- vb uint16
- vbuuid uint64
- hostname string
- jobType JobType
- failover uint8
- lastPersistedSeqNo uint64
- currentSeqNo uint64
- resultChan chan *ObservePersistJob
- errorChan chan *OPErrResponse
- }
- type OPErrResponse struct {
- vb uint16
- vbuuid uint64
- err error
- job *ObservePersistJob
- }
- var ObservePersistPool = NewPool(1024)
- var OPJobChan = make(chan *ObservePersistJob, 1024)
- var OPJobDone = make(chan bool)
- var wg sync.WaitGroup
- func (b *Bucket) StartOPPollers(maxWorkers int) {
- for i := 0; i < maxWorkers; i++ {
- go b.OPJobPoll()
- wg.Add(1)
- }
- wg.Wait()
- }
- func (b *Bucket) SetObserveAndPersist(nPersist PersistTo, nObserve ObserveTo) (err error) {
- numNodes := len(b.Nodes())
- if int(nPersist) > numNodes || int(nObserve) > numNodes {
- return fmt.Errorf("Not enough healthy nodes in the cluster")
- }
- if int(nPersist) > (b.Replicas+1) || int(nObserve) > b.Replicas {
- return fmt.Errorf("Not enough replicas in the cluster")
- }
- if EnableMutationToken == false {
- return fmt.Errorf("Mutation Tokens not enabled ")
- }
- b.ds = &DurablitySettings{Persist: PersistTo(nPersist), Observe: ObserveTo(nObserve)}
- return
- }
- func (b *Bucket) ObserveAndPersistPoll(vb uint16, vbuuid uint64, seqNo uint64) (err error, failover bool) {
- b.RLock()
- ds := b.ds
- b.RUnlock()
- if ds == nil {
- return
- }
- nj := 0 // total number of jobs
- resultChan := make(chan *ObservePersistJob, 10)
- errChan := make(chan *OPErrResponse, 10)
- nodes := b.GetNodeList(vb)
- if int(ds.Observe) > len(nodes) || int(ds.Persist) > len(nodes) {
- return fmt.Errorf("Not enough healthy nodes in the cluster"), false
- }
- logging.Infof("Node list %v", nodes)
- if ds.Observe >= ObserveReplicateOne {
- // create a job for each host
- for i := ObserveReplicateOne; i < ds.Observe+1; i++ {
- opJob := ObservePersistPool.Get()
- opJob.vb = vb
- opJob.vbuuid = vbuuid
- opJob.jobType = OBSERVE
- opJob.hostname = nodes[i]
- opJob.resultChan = resultChan
- opJob.errorChan = errChan
- OPJobChan <- opJob
- nj++
- }
- }
- if ds.Persist >= PersistMaster {
- for i := PersistMaster; i < ds.Persist+1; i++ {
- opJob := ObservePersistPool.Get()
- opJob.vb = vb
- opJob.vbuuid = vbuuid
- opJob.jobType = PERSIST
- opJob.hostname = nodes[i]
- opJob.resultChan = resultChan
- opJob.errorChan = errChan
- OPJobChan <- opJob
- nj++
- }
- }
- ok := true
- for ok {
- select {
- case res := <-resultChan:
- jobDone := false
- if res.failover == 0 {
- // no failover
- if res.jobType == PERSIST {
- if res.lastPersistedSeqNo >= seqNo {
- jobDone = true
- }
- } else {
- if res.currentSeqNo >= seqNo {
- jobDone = true
- }
- }
- if jobDone == true {
- nj--
- ObservePersistPool.Put(res)
- } else {
- // requeue this job
- OPJobChan <- res
- }
- } else {
- // Not currently handling failover scenarios TODO
- nj--
- ObservePersistPool.Put(res)
- failover = true
- }
- if nj == 0 {
- // done with all the jobs
- ok = false
- close(resultChan)
- close(errChan)
- }
- case Err := <-errChan:
- logging.Errorf("Error in Observe/Persist %v", Err.err)
- err = fmt.Errorf("Error in Observe/Persist job %v", Err.err)
- nj--
- ObservePersistPool.Put(Err.job)
- if nj == 0 {
- close(resultChan)
- close(errChan)
- ok = false
- }
- }
- }
- return
- }
- func (b *Bucket) OPJobPoll() {
- ok := true
- for ok == true {
- select {
- case job := <-OPJobChan:
- pool := b.getConnPoolByHost(job.hostname, false /* bucket not already locked */)
- if pool == nil {
- errRes := &OPErrResponse{vb: job.vb, vbuuid: job.vbuuid}
- errRes.err = fmt.Errorf("Pool not found for host %v", job.hostname)
- errRes.job = job
- job.errorChan <- errRes
- continue
- }
- conn, err := pool.Get()
- if err != nil {
- errRes := &OPErrResponse{vb: job.vb, vbuuid: job.vbuuid}
- errRes.err = fmt.Errorf("Unable to get connection from pool %v", err)
- errRes.job = job
- job.errorChan <- errRes
- continue
- }
- res, err := conn.ObserveSeq(job.vb, job.vbuuid)
- if err != nil {
- errRes := &OPErrResponse{vb: job.vb, vbuuid: job.vbuuid}
- errRes.err = fmt.Errorf("Command failed %v", err)
- errRes.job = job
- job.errorChan <- errRes
- continue
- }
- pool.Return(conn)
- job.lastPersistedSeqNo = res.LastPersistedSeqNo
- job.currentSeqNo = res.CurrentSeqNo
- job.failover = res.Failover
- job.resultChan <- job
- case <-OPJobDone:
- logging.Infof("Observe Persist Poller exitting")
- ok = false
- }
- }
- wg.Done()
- }
- func (b *Bucket) GetNodeList(vb uint16) []string {
- vbm := b.VBServerMap()
- if len(vbm.VBucketMap) < int(vb) {
- logging.Infof("vbmap smaller than vblist")
- return nil
- }
- nodes := make([]string, len(vbm.VBucketMap[vb]))
- for i := 0; i < len(vbm.VBucketMap[vb]); i++ {
- n := vbm.VBucketMap[vb][i]
- if n < 0 {
- continue
- }
- node := b.getMasterNode(n)
- if len(node) > 1 {
- nodes[i] = node
- }
- continue
- }
- return nodes
- }
- //pool of ObservePersist Jobs
- type OPpool struct {
- pool chan *ObservePersistJob
- }
- // NewPool creates a new pool of jobs
- func NewPool(max int) *OPpool {
- return &OPpool{
- pool: make(chan *ObservePersistJob, max),
- }
- }
- // Borrow a Client from the pool.
- func (p *OPpool) Get() *ObservePersistJob {
- var o *ObservePersistJob
- select {
- case o = <-p.pool:
- default:
- o = &ObservePersistJob{}
- }
- return o
- }
- // Return returns a Client to the pool.
- func (p *OPpool) Put(o *ObservePersistJob) {
- select {
- case p.pool <- o:
- default:
- // let it go, let it go...
- }
- }
|