// Do execute one or more `BulkOp` items in parallel. func (b *Bucket) Do(ops []BulkOp) error { timeoutTmr := gocbcore.AcquireTimer(time.Second * 10) // Make the channel big enough to hold all our ops in case // we get delayed inside execute (don't want to block the // individual op handlers when they dispatch their signal). signal := make(chan BulkOp, len(ops)) for _, item := range ops { item.execute(b, signal) } for _ = range ops { select { case item := <-signal: // We're really just clearing the pendop from this thread, // since it already completed, no cancel actually occurs item.cancel() case <-timeoutTmr.C: gocbcore.ReleaseTimer(timeoutTmr, true) for _, item := range ops { if !item.cancel() { <-signal } } return ErrTimeout } } gocbcore.ReleaseTimer(timeoutTmr, false) return nil }
func (b *Bucket) getRandom(valuePtr interface{}) (keyOut string, casOut Cas, errOut error) { signal := make(chan bool, 1) op, err := b.client.GetRandom(func(keyBytes, bytes []byte, flags uint32, cas gocbcore.Cas, err error) { errOut = err if errOut == nil { errOut = b.transcoder.Decode(bytes, flags, valuePtr) if errOut == nil { casOut = Cas(cas) keyOut = string(keyBytes) } } signal <- true }) if err != nil { return "", 0, err } timeoutTmr := gocbcore.AcquireTimer(b.opTimeout) select { case <-signal: gocbcore.ReleaseTimer(timeoutTmr, false) return case <-timeoutTmr.C: gocbcore.ReleaseTimer(timeoutTmr, true) op.Cancel() return "", 0, timeoutError{} } }
func (b *Bucket) hlpGetExec(valuePtr interface{}, execFn hlpGetHandler) (casOut Cas, errOut error) { signal := make(chan bool, 1) op, err := execFn(func(bytes []byte, flags uint32, cas gocbcore.Cas, err error) { errOut = err if errOut == nil { errOut = b.transcoder.Decode(bytes, flags, valuePtr) if errOut == nil { casOut = Cas(cas) } } signal <- true }) if err != nil { return 0, err } timeoutTmr := gocbcore.AcquireTimer(b.opTimeout) select { case <-signal: gocbcore.ReleaseTimer(timeoutTmr, false) return case <-timeoutTmr.C: gocbcore.ReleaseTimer(timeoutTmr, true) if !op.Cancel() { <-signal return } return 0, ErrTimeout } }
func (b *Bucket) hlpCtrExec(execFn hlpCtrHandler) (valOut uint64, casOut Cas, mtOut MutationToken, errOut error) { signal := make(chan bool, 1) op, err := execFn(func(value uint64, cas gocbcore.Cas, mt gocbcore.MutationToken, err error) { errOut = err if errOut == nil { valOut = value casOut = Cas(cas) mtOut = MutationToken(mt) } signal <- true }) if err != nil { return 0, 0, MutationToken{}, err } timeoutTmr := gocbcore.AcquireTimer(b.opTimeout) select { case <-signal: gocbcore.ReleaseTimer(timeoutTmr, false) return case <-timeoutTmr.C: gocbcore.ReleaseTimer(timeoutTmr, true) if !op.Cancel() { <-signal return } return 0, 0, MutationToken{}, ErrTimeout } }
func (b *Bucket) hlpCasExec(execFn hlpCasHandler) (casOut Cas, mtOut MutationToken, errOut error) { signal := make(chan bool, 1) op, err := execFn(func(cas gocbcore.Cas, mt gocbcore.MutationToken, err error) { errOut = err if errOut == nil { casOut = Cas(cas) mtOut = MutationToken(mt) } signal <- true }) if err != nil { return 0, MutationToken{}, err } timeoutTmr := gocbcore.AcquireTimer(b.opTimeout) select { case <-signal: gocbcore.ReleaseTimer(timeoutTmr, false) return case <-timeoutTmr.C: gocbcore.ReleaseTimer(timeoutTmr, true) op.Cancel() return 0, MutationToken{}, timeoutError{} } }
func (b *Bucket) mutateIn(set *MutateInBuilder) (resOut *DocumentFragment, errOut error) { signal := make(chan bool, 1) op, err := b.client.SubDocMutate([]byte(set.name), set.ops, set.cas, set.expiry, func(results []gocbcore.SubDocResult, cas gocbcore.Cas, mt gocbcore.MutationToken, err error) { errOut = err if errOut == nil { resSet := &DocumentFragment{ cas: Cas(cas), mt: MutationToken{mt, b}, } resSet.contents = make([]subDocResult, len(results)) for i := range results { resSet.contents[i].path = set.ops[i].Path resSet.contents[i].err = results[i].Err if results[i].Value != nil { resSet.contents[i].data = append([]byte(nil), results[i].Value...) } } resOut = resSet } signal <- true }) if err != nil { return nil, err } timeoutTmr := gocbcore.AcquireTimer(b.opTimeout) select { case <-signal: gocbcore.ReleaseTimer(timeoutTmr, false) return case <-timeoutTmr.C: gocbcore.ReleaseTimer(timeoutTmr, true) if !op.Cancel() { <-signal return } return nil, ErrTimeout } }
func (b *Bucket) observeOne(key []byte, mt MutationToken, cas Cas, forDelete bool, repId int, replicaCh, persistCh chan bool) { observeOnce := func(commCh chan uint) (pendingOp, error) { if mt.VbUuid != 0 && mt.SeqNo != 0 { return b.observeOnceSeqNo(key, mt, repId, commCh) } else { return b.observeOnceCas(key, cas, forDelete, repId, commCh) } } sentReplicated := false sentPersisted := false failMe := func() { if !sentReplicated { replicaCh <- false sentReplicated = true } if !sentPersisted { persistCh <- false sentPersisted = true } } timeoutTmr := gocbcore.AcquireTimer(b.duraTimeout) commCh := make(chan uint) for { op, err := observeOnce(commCh) if err != nil { gocbcore.ReleaseTimer(timeoutTmr, false) failMe() return } select { case val := <-commCh: // Got Value if (val&1) != 0 && !sentReplicated { replicaCh <- true sentReplicated = true } if (val&2) != 0 && !sentPersisted { persistCh <- true sentPersisted = true } waitTmr := gocbcore.AcquireTimer(b.duraPollTimeout) select { case <-waitTmr.C: gocbcore.ReleaseTimer(waitTmr, true) // Fall through to outside for loop case <-timeoutTmr.C: gocbcore.ReleaseTimer(waitTmr, false) gocbcore.ReleaseTimer(timeoutTmr, true) failMe() return } case <-timeoutTmr.C: // Timed out op.Cancel() gocbcore.ReleaseTimer(timeoutTmr, true) failMe() return } } }