You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1477 lines
38 KiB

  1. /*
  2. Package couchbase provides a smart client for go.
  3. Usage:
  4. client, err := couchbase.Connect("http://myserver:8091/")
  5. handleError(err)
  6. pool, err := client.GetPool("default")
  7. handleError(err)
  8. bucket, err := pool.GetBucket("MyAwesomeBucket")
  9. handleError(err)
  10. ...
  11. or a shortcut for the bucket directly
  12. bucket, err := couchbase.GetBucket("http://myserver:8091/", "default", "default")
  13. in any case, you can specify authentication credentials using
  14. standard URL userinfo syntax:
  15. b, err := couchbase.GetBucket("http://bucketname:bucketpass@myserver:8091/",
  16. "default", "bucket")
  17. */
  18. package couchbase
  19. import (
  20. "encoding/binary"
  21. "encoding/json"
  22. "errors"
  23. "fmt"
  24. "io"
  25. "runtime"
  26. "strconv"
  27. "strings"
  28. "sync"
  29. "time"
  30. "unsafe"
  31. "github.com/couchbase/gomemcached"
  32. "github.com/couchbase/gomemcached/client" // package name is 'memcached'
  33. "github.com/couchbase/goutils/logging"
  34. )
  35. // Mutation Token
  36. type MutationToken struct {
  37. VBid uint16 // vbucket id
  38. Guard uint64 // vbuuid
  39. Value uint64 // sequence number
  40. }
  41. // Maximum number of times to retry a chunk of a bulk get on error.
  42. var MaxBulkRetries = 5000
  43. var backOffDuration time.Duration = 100 * time.Millisecond
  44. var MaxBackOffRetries = 25 // exponentail backOff result in over 30sec (25*13*0.1s)
  45. // If this is set to a nonzero duration, Do() and ViewCustom() will log a warning if the call
  46. // takes longer than that.
  47. var SlowServerCallWarningThreshold time.Duration
  48. func slowLog(startTime time.Time, format string, args ...interface{}) {
  49. if elapsed := time.Now().Sub(startTime); elapsed > SlowServerCallWarningThreshold {
  50. pc, _, _, _ := runtime.Caller(2)
  51. caller := runtime.FuncForPC(pc).Name()
  52. logging.Infof("go-couchbase: "+format+" in "+caller+" took "+elapsed.String(), args...)
  53. }
  54. }
  55. // Return true if error is KEY_ENOENT. Required by cbq-engine
  56. func IsKeyEExistsError(err error) bool {
  57. res, ok := err.(*gomemcached.MCResponse)
  58. if ok && res.Status == gomemcached.KEY_EEXISTS {
  59. return true
  60. }
  61. return false
  62. }
  63. // Return true if error is KEY_ENOENT. Required by cbq-engine
  64. func IsKeyNoEntError(err error) bool {
  65. res, ok := err.(*gomemcached.MCResponse)
  66. if ok && res.Status == gomemcached.KEY_ENOENT {
  67. return true
  68. }
  69. return false
  70. }
  71. // Return true if error suggests a bucket refresh is required. Required by cbq-engine
  72. func IsRefreshRequired(err error) bool {
  73. res, ok := err.(*gomemcached.MCResponse)
  74. if ok && (res.Status == gomemcached.NO_BUCKET || res.Status == gomemcached.NOT_MY_VBUCKET) {
  75. return true
  76. }
  77. return false
  78. }
  79. // ClientOpCallback is called for each invocation of Do.
  80. var ClientOpCallback func(opname, k string, start time.Time, err error)
  81. // Do executes a function on a memcached connection to the node owning key "k"
  82. //
  83. // Note that this automatically handles transient errors by replaying
  84. // your function on a "not-my-vbucket" error, so don't assume
  85. // your command will only be executed only once.
  86. func (b *Bucket) Do(k string, f func(mc *memcached.Client, vb uint16) error) (err error) {
  87. return b.Do2(k, f, true)
  88. }
  89. func (b *Bucket) Do2(k string, f func(mc *memcached.Client, vb uint16) error, deadline bool) (err error) {
  90. if SlowServerCallWarningThreshold > 0 {
  91. defer slowLog(time.Now(), "call to Do(%q)", k)
  92. }
  93. vb := b.VBHash(k)
  94. maxTries := len(b.Nodes()) * 2
  95. for i := 0; i < maxTries; i++ {
  96. conn, pool, err := b.getConnectionToVBucket(vb)
  97. if err != nil {
  98. if isConnError(err) && backOff(i, maxTries, backOffDuration, true) {
  99. b.Refresh()
  100. continue
  101. }
  102. return err
  103. }
  104. if deadline && DefaultTimeout > 0 {
  105. conn.SetDeadline(getDeadline(noDeadline, DefaultTimeout))
  106. err = f(conn, uint16(vb))
  107. conn.SetDeadline(noDeadline)
  108. } else {
  109. err = f(conn, uint16(vb))
  110. }
  111. var retry bool
  112. discard := isOutOfBoundsError(err)
  113. // MB-30967 / MB-31001 implement back off for transient errors
  114. if resp, ok := err.(*gomemcached.MCResponse); ok {
  115. switch resp.Status {
  116. case gomemcached.NOT_MY_VBUCKET:
  117. b.Refresh()
  118. // MB-28842: in case of NMVB, check if the node is still part of the map
  119. // and ditch the connection if it isn't.
  120. discard = b.checkVBmap(pool.Node())
  121. retry = true
  122. case gomemcached.NOT_SUPPORTED:
  123. discard = true
  124. retry = true
  125. case gomemcached.ENOMEM:
  126. fallthrough
  127. case gomemcached.TMPFAIL:
  128. retry = backOff(i, maxTries, backOffDuration, true)
  129. default:
  130. retry = false
  131. }
  132. } else if err != nil && isConnError(err) && backOff(i, maxTries, backOffDuration, true) {
  133. retry = true
  134. }
  135. if discard {
  136. pool.Discard(conn)
  137. } else {
  138. pool.Return(conn)
  139. }
  140. if !retry {
  141. return err
  142. }
  143. }
  144. return fmt.Errorf("unable to complete action after %v attemps", maxTries)
  145. }
  146. type GatheredStats struct {
  147. Server string
  148. Stats map[string]string
  149. Err error
  150. }
  151. func getStatsParallel(sn string, b *Bucket, offset int, which string,
  152. ch chan<- GatheredStats) {
  153. pool := b.getConnPool(offset)
  154. var gatheredStats GatheredStats
  155. conn, err := pool.Get()
  156. defer func() {
  157. pool.Return(conn)
  158. ch <- gatheredStats
  159. }()
  160. if err != nil {
  161. gatheredStats = GatheredStats{Server: sn, Err: err}
  162. } else {
  163. sm, err := conn.StatsMap(which)
  164. gatheredStats = GatheredStats{Server: sn, Stats: sm, Err: err}
  165. }
  166. }
  167. // GetStats gets a set of stats from all servers.
  168. //
  169. // Returns a map of server ID -> map of stat key to map value.
  170. func (b *Bucket) GetStats(which string) map[string]map[string]string {
  171. rv := map[string]map[string]string{}
  172. for server, gs := range b.GatherStats(which) {
  173. if len(gs.Stats) > 0 {
  174. rv[server] = gs.Stats
  175. }
  176. }
  177. return rv
  178. }
  179. // GatherStats returns a map of server ID -> GatheredStats from all servers.
  180. func (b *Bucket) GatherStats(which string) map[string]GatheredStats {
  181. vsm := b.VBServerMap()
  182. if vsm.ServerList == nil {
  183. return nil
  184. }
  185. // Go grab all the things at once.
  186. ch := make(chan GatheredStats, len(vsm.ServerList))
  187. for i, sn := range vsm.ServerList {
  188. go getStatsParallel(sn, b, i, which, ch)
  189. }
  190. // Gather the results
  191. rv := map[string]GatheredStats{}
  192. for range vsm.ServerList {
  193. gs := <-ch
  194. rv[gs.Server] = gs
  195. }
  196. return rv
  197. }
  198. // Get bucket count through the bucket stats
  199. func (b *Bucket) GetCount(refresh bool) (count int64, err error) {
  200. if refresh {
  201. b.Refresh()
  202. }
  203. var cnt int64
  204. for _, gs := range b.GatherStats("") {
  205. if len(gs.Stats) > 0 {
  206. cnt, err = strconv.ParseInt(gs.Stats["curr_items"], 10, 64)
  207. if err != nil {
  208. return 0, err
  209. }
  210. count += cnt
  211. }
  212. }
  213. return count, nil
  214. }
  215. // Get bucket document size through the bucket stats
  216. func (b *Bucket) GetSize(refresh bool) (size int64, err error) {
  217. if refresh {
  218. b.Refresh()
  219. }
  220. var sz int64
  221. for _, gs := range b.GatherStats("") {
  222. if len(gs.Stats) > 0 {
  223. sz, err = strconv.ParseInt(gs.Stats["ep_value_size"], 10, 64)
  224. if err != nil {
  225. return 0, err
  226. }
  227. size += sz
  228. }
  229. }
  230. return size, nil
  231. }
  232. func isAuthError(err error) bool {
  233. estr := err.Error()
  234. return strings.Contains(estr, "Auth failure")
  235. }
  236. func IsReadTimeOutError(err error) bool {
  237. estr := err.Error()
  238. return strings.Contains(estr, "read tcp") ||
  239. strings.Contains(estr, "i/o timeout")
  240. }
  241. func isTimeoutError(err error) bool {
  242. estr := err.Error()
  243. return strings.Contains(estr, "i/o timeout") ||
  244. strings.Contains(estr, "connection timed out") ||
  245. strings.Contains(estr, "no route to host")
  246. }
  247. // Errors that are not considered fatal for our fetch loop
  248. func isConnError(err error) bool {
  249. if err == io.EOF {
  250. return true
  251. }
  252. estr := err.Error()
  253. return strings.Contains(estr, "broken pipe") ||
  254. strings.Contains(estr, "connection reset") ||
  255. strings.Contains(estr, "connection refused") ||
  256. strings.Contains(estr, "connection pool is closed")
  257. }
  258. func isOutOfBoundsError(err error) bool {
  259. return err != nil && strings.Contains(err.Error(), "Out of Bounds error")
  260. }
  261. func getDeadline(reqDeadline time.Time, duration time.Duration) time.Time {
  262. if reqDeadline.IsZero() && duration > 0 {
  263. return time.Now().Add(duration)
  264. }
  265. return reqDeadline
  266. }
  267. func backOff(attempt, maxAttempts int, duration time.Duration, exponential bool) bool {
  268. if attempt < maxAttempts {
  269. // 0th attempt return immediately
  270. if attempt > 0 {
  271. if exponential {
  272. duration = time.Duration(attempt) * duration
  273. }
  274. time.Sleep(duration)
  275. }
  276. return true
  277. }
  278. return false
  279. }
  280. func (b *Bucket) doBulkGet(vb uint16, keys []string, reqDeadline time.Time,
  281. ch chan<- map[string]*gomemcached.MCResponse, ech chan<- error, subPaths []string,
  282. eStatus *errorStatus) {
  283. if SlowServerCallWarningThreshold > 0 {
  284. defer slowLog(time.Now(), "call to doBulkGet(%d, %d keys)", vb, len(keys))
  285. }
  286. rv := _STRING_MCRESPONSE_POOL.Get()
  287. attempts := 0
  288. backOffAttempts := 0
  289. done := false
  290. bname := b.Name
  291. for ; attempts < MaxBulkRetries && !done && !eStatus.errStatus; attempts++ {
  292. if len(b.VBServerMap().VBucketMap) < int(vb) {
  293. //fatal
  294. err := fmt.Errorf("vbmap smaller than requested for %v", bname)
  295. logging.Errorf("go-couchbase: %v vb %d vbmap len %d", err.Error(), vb, len(b.VBServerMap().VBucketMap))
  296. ech <- err
  297. return
  298. }
  299. masterID := b.VBServerMap().VBucketMap[vb][0]
  300. if masterID < 0 {
  301. // fatal
  302. err := fmt.Errorf("No master node available for %v vb %d", bname, vb)
  303. logging.Errorf("%v", err.Error())
  304. ech <- err
  305. return
  306. }
  307. // This stack frame exists to ensure we can clean up
  308. // connection at a reasonable time.
  309. err := func() error {
  310. pool := b.getConnPool(masterID)
  311. conn, err := pool.Get()
  312. if err != nil {
  313. if isAuthError(err) || isTimeoutError(err) {
  314. logging.Errorf("Fatal Error %v : %v", bname, err)
  315. ech <- err
  316. return err
  317. } else if isConnError(err) {
  318. if !backOff(backOffAttempts, MaxBackOffRetries, backOffDuration, true) {
  319. logging.Errorf("Connection Error %v : %v", bname, err)
  320. ech <- err
  321. return err
  322. }
  323. b.Refresh()
  324. backOffAttempts++
  325. }
  326. logging.Infof("Pool Get returned %v: %v", bname, err)
  327. // retry
  328. return nil
  329. }
  330. conn.SetDeadline(getDeadline(reqDeadline, DefaultTimeout))
  331. err = conn.GetBulk(vb, keys, rv, subPaths)
  332. conn.SetDeadline(noDeadline)
  333. discard := false
  334. defer func() {
  335. if discard {
  336. pool.Discard(conn)
  337. } else {
  338. pool.Return(conn)
  339. }
  340. }()
  341. switch err.(type) {
  342. case *gomemcached.MCResponse:
  343. notSMaxTries := len(b.Nodes()) * 2
  344. st := err.(*gomemcached.MCResponse).Status
  345. if st == gomemcached.NOT_MY_VBUCKET || (st == gomemcached.NOT_SUPPORTED && attempts < notSMaxTries) {
  346. b.Refresh()
  347. discard = b.checkVBmap(pool.Node())
  348. return nil // retry
  349. } else if st == gomemcached.EBUSY || st == gomemcached.LOCKED {
  350. if (attempts % (MaxBulkRetries / 100)) == 0 {
  351. logging.Infof("Retrying Memcached error (%v) FOR %v(vbid:%d, keys:<ud>%v</ud>)",
  352. err.Error(), bname, vb, keys)
  353. }
  354. return nil // retry
  355. } else if (st == gomemcached.ENOMEM || st == gomemcached.TMPFAIL) && backOff(backOffAttempts, MaxBackOffRetries, backOffDuration, true) {
  356. // MB-30967 / MB-31001 use backoff for TMPFAIL too
  357. backOffAttempts++
  358. logging.Infof("Retrying Memcached error (%v) FOR %v(vbid:%d, keys:<ud>%v</ud>)",
  359. err.Error(), bname, vb, keys)
  360. return nil // retry
  361. }
  362. ech <- err
  363. return err
  364. case error:
  365. if isOutOfBoundsError(err) {
  366. // We got an out of bound error, retry the operation
  367. discard = true
  368. return nil
  369. } else if isConnError(err) && backOff(backOffAttempts, MaxBackOffRetries, backOffDuration, true) {
  370. backOffAttempts++
  371. logging.Errorf("Connection Error: %s. Refreshing bucket %v (vbid:%v,keys:<ud>%v</ud>)",
  372. err.Error(), bname, vb, keys)
  373. discard = true
  374. b.Refresh()
  375. return nil // retry
  376. }
  377. ech <- err
  378. ch <- rv
  379. return err
  380. }
  381. done = true
  382. return nil
  383. }()
  384. if err != nil {
  385. return
  386. }
  387. }
  388. if attempts >= MaxBulkRetries {
  389. err := fmt.Errorf("bulkget exceeded MaxBulkRetries for %v(vbid:%d,keys:<ud>%v</ud>)", bname, vb, keys)
  390. logging.Errorf("%v", err.Error())
  391. ech <- err
  392. }
  393. ch <- rv
  394. }
  395. type errorStatus struct {
  396. errStatus bool
  397. }
  398. type vbBulkGet struct {
  399. b *Bucket
  400. ch chan<- map[string]*gomemcached.MCResponse
  401. ech chan<- error
  402. k uint16
  403. keys []string
  404. reqDeadline time.Time
  405. wg *sync.WaitGroup
  406. subPaths []string
  407. groupError *errorStatus
  408. }
  409. const _NUM_CHANNELS = 5
  410. var _NUM_CHANNEL_WORKERS = (runtime.NumCPU() + 1) / 2
  411. var DefaultDialTimeout = time.Duration(0)
  412. var DefaultTimeout = time.Duration(0)
  413. var noDeadline = time.Time{}
  414. // Buffer 4k requests per worker
  415. var _VB_BULK_GET_CHANNELS []chan *vbBulkGet
  416. func InitBulkGet() {
  417. DefaultDialTimeout = 20 * time.Second
  418. DefaultTimeout = 120 * time.Second
  419. memcached.SetDefaultDialTimeout(DefaultDialTimeout)
  420. _VB_BULK_GET_CHANNELS = make([]chan *vbBulkGet, _NUM_CHANNELS)
  421. for i := 0; i < _NUM_CHANNELS; i++ {
  422. channel := make(chan *vbBulkGet, 16*1024*_NUM_CHANNEL_WORKERS)
  423. _VB_BULK_GET_CHANNELS[i] = channel
  424. for j := 0; j < _NUM_CHANNEL_WORKERS; j++ {
  425. go vbBulkGetWorker(channel)
  426. }
  427. }
  428. }
  429. func vbBulkGetWorker(ch chan *vbBulkGet) {
  430. defer func() {
  431. // Workers cannot panic and die
  432. recover()
  433. go vbBulkGetWorker(ch)
  434. }()
  435. for vbg := range ch {
  436. vbDoBulkGet(vbg)
  437. }
  438. }
  439. func vbDoBulkGet(vbg *vbBulkGet) {
  440. defer vbg.wg.Done()
  441. defer func() {
  442. // Workers cannot panic and die
  443. recover()
  444. }()
  445. vbg.b.doBulkGet(vbg.k, vbg.keys, vbg.reqDeadline, vbg.ch, vbg.ech, vbg.subPaths, vbg.groupError)
  446. }
  447. var _ERR_CHAN_FULL = fmt.Errorf("Data request queue full, aborting query.")
  448. func (b *Bucket) processBulkGet(kdm map[uint16][]string, reqDeadline time.Time,
  449. ch chan<- map[string]*gomemcached.MCResponse, ech chan<- error, subPaths []string,
  450. eStatus *errorStatus) {
  451. defer close(ch)
  452. defer close(ech)
  453. wg := &sync.WaitGroup{}
  454. for k, keys := range kdm {
  455. // GetBulk() group has error donot Queue items for this group
  456. if eStatus.errStatus {
  457. break
  458. }
  459. vbg := &vbBulkGet{
  460. b: b,
  461. ch: ch,
  462. ech: ech,
  463. k: k,
  464. keys: keys,
  465. reqDeadline: reqDeadline,
  466. wg: wg,
  467. subPaths: subPaths,
  468. groupError: eStatus,
  469. }
  470. wg.Add(1)
  471. // Random int
  472. // Right shift to avoid 8-byte alignment, and take low bits
  473. c := (uintptr(unsafe.Pointer(vbg)) >> 4) % _NUM_CHANNELS
  474. select {
  475. case _VB_BULK_GET_CHANNELS[c] <- vbg:
  476. // No-op
  477. default:
  478. // Buffer full, abandon the bulk get
  479. ech <- _ERR_CHAN_FULL
  480. wg.Add(-1)
  481. }
  482. }
  483. // Wait for my vb bulk gets
  484. wg.Wait()
  485. }
  486. type multiError []error
  487. func (m multiError) Error() string {
  488. if len(m) == 0 {
  489. panic("Error of none")
  490. }
  491. return fmt.Sprintf("{%v errors, starting with %v}", len(m), m[0].Error())
  492. }
  493. // Convert a stream of errors from ech into a multiError (or nil) and
  494. // send down eout.
  495. //
  496. // At least one send is guaranteed on eout, but two is possible, so
  497. // buffer the out channel appropriately.
  498. func errorCollector(ech <-chan error, eout chan<- error, eStatus *errorStatus) {
  499. defer func() { eout <- nil }()
  500. var errs multiError
  501. for e := range ech {
  502. if !eStatus.errStatus && !IsKeyNoEntError(e) {
  503. eStatus.errStatus = true
  504. }
  505. errs = append(errs, e)
  506. }
  507. if len(errs) > 0 {
  508. eout <- errs
  509. }
  510. }
  511. // Fetches multiple keys concurrently, with []byte values
  512. //
  513. // This is a wrapper around GetBulk which converts all values returned
  514. // by GetBulk from raw memcached responses into []byte slices.
  515. // Returns one document for duplicate keys
  516. func (b *Bucket) GetBulkRaw(keys []string) (map[string][]byte, error) {
  517. resp, eout := b.getBulk(keys, noDeadline, nil)
  518. rv := make(map[string][]byte, len(keys))
  519. for k, av := range resp {
  520. rv[k] = av.Body
  521. }
  522. b.ReleaseGetBulkPools(resp)
  523. return rv, eout
  524. }
  525. // GetBulk fetches multiple keys concurrently.
  526. //
  527. // Unlike more convenient GETs, the entire response is returned in the
  528. // map array for each key. Keys that were not found will not be included in
  529. // the map.
  530. func (b *Bucket) GetBulk(keys []string, reqDeadline time.Time, subPaths []string) (map[string]*gomemcached.MCResponse, error) {
  531. return b.getBulk(keys, reqDeadline, subPaths)
  532. }
  533. func (b *Bucket) ReleaseGetBulkPools(rv map[string]*gomemcached.MCResponse) {
  534. _STRING_MCRESPONSE_POOL.Put(rv)
  535. }
  536. func (b *Bucket) getBulk(keys []string, reqDeadline time.Time, subPaths []string) (map[string]*gomemcached.MCResponse, error) {
  537. kdm := _VB_STRING_POOL.Get()
  538. defer _VB_STRING_POOL.Put(kdm)
  539. for _, k := range keys {
  540. if k != "" {
  541. vb := uint16(b.VBHash(k))
  542. a, ok1 := kdm[vb]
  543. if !ok1 {
  544. a = _STRING_POOL.Get()
  545. }
  546. kdm[vb] = append(a, k)
  547. }
  548. }
  549. eout := make(chan error, 2)
  550. groupErrorStatus := &errorStatus{}
  551. // processBulkGet will own both of these channels and
  552. // guarantee they're closed before it returns.
  553. ch := make(chan map[string]*gomemcached.MCResponse)
  554. ech := make(chan error)
  555. go errorCollector(ech, eout, groupErrorStatus)
  556. go b.processBulkGet(kdm, reqDeadline, ch, ech, subPaths, groupErrorStatus)
  557. var rv map[string]*gomemcached.MCResponse
  558. for m := range ch {
  559. if rv == nil {
  560. rv = m
  561. continue
  562. }
  563. for k, v := range m {
  564. rv[k] = v
  565. }
  566. _STRING_MCRESPONSE_POOL.Put(m)
  567. }
  568. return rv, <-eout
  569. }
  570. // WriteOptions is the set of option flags availble for the Write
  571. // method. They are ORed together to specify the desired request.
  572. type WriteOptions int
  573. const (
  574. // Raw specifies that the value is raw []byte or nil; don't
  575. // JSON-encode it.
  576. Raw = WriteOptions(1 << iota)
  577. // AddOnly indicates an item should only be written if it
  578. // doesn't exist, otherwise ErrKeyExists is returned.
  579. AddOnly
  580. // Persist causes the operation to block until the server
  581. // confirms the item is persisted.
  582. Persist
  583. // Indexable causes the operation to block until it's availble via the index.
  584. Indexable
  585. // Append indicates the given value should be appended to the
  586. // existing value for the given key.
  587. Append
  588. )
  589. var optNames = []struct {
  590. opt WriteOptions
  591. name string
  592. }{
  593. {Raw, "raw"},
  594. {AddOnly, "addonly"}, {Persist, "persist"},
  595. {Indexable, "indexable"}, {Append, "append"},
  596. }
  597. // String representation of WriteOptions
  598. func (w WriteOptions) String() string {
  599. f := []string{}
  600. for _, on := range optNames {
  601. if w&on.opt != 0 {
  602. f = append(f, on.name)
  603. w &= ^on.opt
  604. }
  605. }
  606. if len(f) == 0 || w != 0 {
  607. f = append(f, fmt.Sprintf("0x%x", int(w)))
  608. }
  609. return strings.Join(f, "|")
  610. }
  611. // Error returned from Write with AddOnly flag, when key already exists in the bucket.
  612. var ErrKeyExists = errors.New("key exists")
  613. // General-purpose value setter.
  614. //
  615. // The Set, Add and Delete methods are just wrappers around this. The
  616. // interpretation of `v` depends on whether the `Raw` option is
  617. // given. If it is, v must be a byte array or nil. (A nil value causes
  618. // a delete.) If `Raw` is not given, `v` will be marshaled as JSON
  619. // before being written. It must be JSON-marshalable and it must not
  620. // be nil.
  621. func (b *Bucket) Write(k string, flags, exp int, v interface{},
  622. opt WriteOptions) (err error) {
  623. if ClientOpCallback != nil {
  624. defer func(t time.Time) {
  625. ClientOpCallback(fmt.Sprintf("Write(%v)", opt), k, t, err)
  626. }(time.Now())
  627. }
  628. var data []byte
  629. if opt&Raw == 0 {
  630. data, err = json.Marshal(v)
  631. if err != nil {
  632. return err
  633. }
  634. } else if v != nil {
  635. data = v.([]byte)
  636. }
  637. var res *gomemcached.MCResponse
  638. err = b.Do(k, func(mc *memcached.Client, vb uint16) error {
  639. if opt&AddOnly != 0 {
  640. res, err = memcached.UnwrapMemcachedError(
  641. mc.Add(vb, k, flags, exp, data))
  642. if err == nil && res.Status != gomemcached.SUCCESS {
  643. if res.Status == gomemcached.KEY_EEXISTS {
  644. err = ErrKeyExists
  645. } else {
  646. err = res
  647. }
  648. }
  649. } else if opt&Append != 0 {
  650. res, err = mc.Append(vb, k, data)
  651. } else if data == nil {
  652. res, err = mc.Del(vb, k)
  653. } else {
  654. res, err = mc.Set(vb, k, flags, exp, data)
  655. }
  656. return err
  657. })
  658. if err == nil && (opt&(Persist|Indexable) != 0) {
  659. err = b.WaitForPersistence(k, res.Cas, data == nil)
  660. }
  661. return err
  662. }
  663. func (b *Bucket) WriteWithMT(k string, flags, exp int, v interface{},
  664. opt WriteOptions) (mt *MutationToken, err error) {
  665. if ClientOpCallback != nil {
  666. defer func(t time.Time) {
  667. ClientOpCallback(fmt.Sprintf("WriteWithMT(%v)", opt), k, t, err)
  668. }(time.Now())
  669. }
  670. var data []byte
  671. if opt&Raw == 0 {
  672. data, err = json.Marshal(v)
  673. if err != nil {
  674. return nil, err
  675. }
  676. } else if v != nil {
  677. data = v.([]byte)
  678. }
  679. var res *gomemcached.MCResponse
  680. err = b.Do(k, func(mc *memcached.Client, vb uint16) error {
  681. if opt&AddOnly != 0 {
  682. res, err = memcached.UnwrapMemcachedError(
  683. mc.Add(vb, k, flags, exp, data))
  684. if err == nil && res.Status != gomemcached.SUCCESS {
  685. if res.Status == gomemcached.KEY_EEXISTS {
  686. err = ErrKeyExists
  687. } else {
  688. err = res
  689. }
  690. }
  691. } else if opt&Append != 0 {
  692. res, err = mc.Append(vb, k, data)
  693. } else if data == nil {
  694. res, err = mc.Del(vb, k)
  695. } else {
  696. res, err = mc.Set(vb, k, flags, exp, data)
  697. }
  698. if len(res.Extras) >= 16 {
  699. vbuuid := uint64(binary.BigEndian.Uint64(res.Extras[0:8]))
  700. seqNo := uint64(binary.BigEndian.Uint64(res.Extras[8:16]))
  701. mt = &MutationToken{VBid: vb, Guard: vbuuid, Value: seqNo}
  702. }
  703. return err
  704. })
  705. if err == nil && (opt&(Persist|Indexable) != 0) {
  706. err = b.WaitForPersistence(k, res.Cas, data == nil)
  707. }
  708. return mt, err
  709. }
  710. // Set a value in this bucket with Cas and return the new Cas value
  711. func (b *Bucket) Cas(k string, exp int, cas uint64, v interface{}) (uint64, error) {
  712. return b.WriteCas(k, 0, exp, cas, v, 0)
  713. }
  714. // Set a value in this bucket with Cas without json encoding it
  715. func (b *Bucket) CasRaw(k string, exp int, cas uint64, v interface{}) (uint64, error) {
  716. return b.WriteCas(k, 0, exp, cas, v, Raw)
  717. }
  718. func (b *Bucket) WriteCas(k string, flags, exp int, cas uint64, v interface{},
  719. opt WriteOptions) (newCas uint64, err error) {
  720. if ClientOpCallback != nil {
  721. defer func(t time.Time) {
  722. ClientOpCallback(fmt.Sprintf("Write(%v)", opt), k, t, err)
  723. }(time.Now())
  724. }
  725. var data []byte
  726. if opt&Raw == 0 {
  727. data, err = json.Marshal(v)
  728. if err != nil {
  729. return 0, err
  730. }
  731. } else if v != nil {
  732. data = v.([]byte)
  733. }
  734. var res *gomemcached.MCResponse
  735. err = b.Do(k, func(mc *memcached.Client, vb uint16) error {
  736. res, err = mc.SetCas(vb, k, flags, exp, cas, data)
  737. return err
  738. })
  739. if err == nil && (opt&(Persist|Indexable) != 0) {
  740. err = b.WaitForPersistence(k, res.Cas, data == nil)
  741. }
  742. return res.Cas, err
  743. }
  744. // Extended CAS operation. These functions will return the mutation token, i.e vbuuid & guard
  745. func (b *Bucket) CasWithMeta(k string, flags int, exp int, cas uint64, v interface{}) (uint64, *MutationToken, error) {
  746. return b.WriteCasWithMT(k, flags, exp, cas, v, 0)
  747. }
  748. func (b *Bucket) CasWithMetaRaw(k string, flags int, exp int, cas uint64, v interface{}) (uint64, *MutationToken, error) {
  749. return b.WriteCasWithMT(k, flags, exp, cas, v, Raw)
  750. }
  751. func (b *Bucket) WriteCasWithMT(k string, flags, exp int, cas uint64, v interface{},
  752. opt WriteOptions) (newCas uint64, mt *MutationToken, err error) {
  753. if ClientOpCallback != nil {
  754. defer func(t time.Time) {
  755. ClientOpCallback(fmt.Sprintf("Write(%v)", opt), k, t, err)
  756. }(time.Now())
  757. }
  758. var data []byte
  759. if opt&Raw == 0 {
  760. data, err = json.Marshal(v)
  761. if err != nil {
  762. return 0, nil, err
  763. }
  764. } else if v != nil {
  765. data = v.([]byte)
  766. }
  767. var res *gomemcached.MCResponse
  768. err = b.Do(k, func(mc *memcached.Client, vb uint16) error {
  769. res, err = mc.SetCas(vb, k, flags, exp, cas, data)
  770. return err
  771. })
  772. if err != nil {
  773. return 0, nil, err
  774. }
  775. // check for extras
  776. if len(res.Extras) >= 16 {
  777. vbuuid := uint64(binary.BigEndian.Uint64(res.Extras[0:8]))
  778. seqNo := uint64(binary.BigEndian.Uint64(res.Extras[8:16]))
  779. vb := b.VBHash(k)
  780. mt = &MutationToken{VBid: uint16(vb), Guard: vbuuid, Value: seqNo}
  781. }
  782. if err == nil && (opt&(Persist|Indexable) != 0) {
  783. err = b.WaitForPersistence(k, res.Cas, data == nil)
  784. }
  785. return res.Cas, mt, err
  786. }
  787. // Set a value in this bucket.
  788. // The value will be serialized into a JSON document.
  789. func (b *Bucket) Set(k string, exp int, v interface{}) error {
  790. return b.Write(k, 0, exp, v, 0)
  791. }
  792. // Set a value in this bucket with with flags
  793. func (b *Bucket) SetWithMeta(k string, flags int, exp int, v interface{}) (*MutationToken, error) {
  794. return b.WriteWithMT(k, flags, exp, v, 0)
  795. }
  796. // SetRaw sets a value in this bucket without JSON encoding it.
  797. func (b *Bucket) SetRaw(k string, exp int, v []byte) error {
  798. return b.Write(k, 0, exp, v, Raw)
  799. }
  800. // Add adds a value to this bucket; like Set except that nothing
  801. // happens if the key exists. The value will be serialized into a
  802. // JSON document.
  803. func (b *Bucket) Add(k string, exp int, v interface{}) (added bool, err error) {
  804. err = b.Write(k, 0, exp, v, AddOnly)
  805. if err == ErrKeyExists {
  806. return false, nil
  807. }
  808. return (err == nil), err
  809. }
  810. // AddRaw adds a value to this bucket; like SetRaw except that nothing
  811. // happens if the key exists. The value will be stored as raw bytes.
  812. func (b *Bucket) AddRaw(k string, exp int, v []byte) (added bool, err error) {
  813. err = b.Write(k, 0, exp, v, AddOnly|Raw)
  814. if err == ErrKeyExists {
  815. return false, nil
  816. }
  817. return (err == nil), err
  818. }
  819. // Add adds a value to this bucket; like Set except that nothing
  820. // happens if the key exists. The value will be serialized into a
  821. // JSON document.
  822. func (b *Bucket) AddWithMT(k string, exp int, v interface{}) (added bool, mt *MutationToken, err error) {
  823. mt, err = b.WriteWithMT(k, 0, exp, v, AddOnly)
  824. if err == ErrKeyExists {
  825. return false, mt, nil
  826. }
  827. return (err == nil), mt, err
  828. }
  829. // AddRaw adds a value to this bucket; like SetRaw except that nothing
  830. // happens if the key exists. The value will be stored as raw bytes.
  831. func (b *Bucket) AddRawWithMT(k string, exp int, v []byte) (added bool, mt *MutationToken, err error) {
  832. mt, err = b.WriteWithMT(k, 0, exp, v, AddOnly|Raw)
  833. if err == ErrKeyExists {
  834. return false, mt, nil
  835. }
  836. return (err == nil), mt, err
  837. }
  838. // Append appends raw data to an existing item.
  839. func (b *Bucket) Append(k string, data []byte) error {
  840. return b.Write(k, 0, 0, data, Append|Raw)
  841. }
  842. func (b *Bucket) GetsMCFromCollection(collUid uint32, key string, reqDeadline time.Time) (*gomemcached.MCResponse, error) {
  843. var err error
  844. var response *gomemcached.MCResponse
  845. if key == "" {
  846. return nil, nil
  847. }
  848. if ClientOpCallback != nil {
  849. defer func(t time.Time) { ClientOpCallback("GetsMCFromCollection", key, t, err) }(time.Now())
  850. }
  851. err = b.Do2(key, func(mc *memcached.Client, vb uint16) error {
  852. var err1 error
  853. mc.SetDeadline(getDeadline(reqDeadline, DefaultTimeout))
  854. _, err1 = mc.SelectBucket(b.Name)
  855. if err1 != nil {
  856. mc.SetDeadline(noDeadline)
  857. return err1
  858. }
  859. mc.SetDeadline(getDeadline(reqDeadline, DefaultTimeout))
  860. response, err1 = mc.GetFromCollection(vb, collUid, key)
  861. if err1 != nil {
  862. mc.SetDeadline(noDeadline)
  863. return err1
  864. }
  865. return nil
  866. }, false)
  867. return response, err
  868. }
  869. // Returns collectionUid, manifestUid, error.
  870. func (b *Bucket) GetCollectionCID(scope string, collection string, reqDeadline time.Time) (uint32, uint32, error) {
  871. var err error
  872. var response *gomemcached.MCResponse
  873. if ClientOpCallback != nil {
  874. defer func(t time.Time) { ClientOpCallback("GetCollectionCID", scope+"."+collection, t, err) }(time.Now())
  875. }
  876. var key = "DUMMY" // Contact any server.
  877. var manifestUid uint32
  878. var collUid uint32
  879. err = b.Do2(key, func(mc *memcached.Client, vb uint16) error {
  880. var err1 error
  881. mc.SetDeadline(getDeadline(reqDeadline, DefaultTimeout))
  882. _, err1 = mc.SelectBucket(b.Name)
  883. if err1 != nil {
  884. mc.SetDeadline(noDeadline)
  885. return err1
  886. }
  887. response, err1 = mc.CollectionsGetCID(scope, collection)
  888. if err1 != nil {
  889. mc.SetDeadline(noDeadline)
  890. return err1
  891. }
  892. manifestUid = binary.BigEndian.Uint32(response.Extras[4:8])
  893. collUid = binary.BigEndian.Uint32(response.Extras[8:12])
  894. return nil
  895. }, false)
  896. return collUid, manifestUid, err
  897. }
  898. // Get a value straight from Memcached
  899. func (b *Bucket) GetsMC(key string, reqDeadline time.Time) (*gomemcached.MCResponse, error) {
  900. var err error
  901. var response *gomemcached.MCResponse
  902. if key == "" {
  903. return nil, nil
  904. }
  905. if ClientOpCallback != nil {
  906. defer func(t time.Time) { ClientOpCallback("GetsMC", key, t, err) }(time.Now())
  907. }
  908. err = b.Do2(key, func(mc *memcached.Client, vb uint16) error {
  909. var err1 error
  910. mc.SetDeadline(getDeadline(reqDeadline, DefaultTimeout))
  911. response, err1 = mc.Get(vb, key)
  912. mc.SetDeadline(noDeadline)
  913. if err1 != nil {
  914. return err1
  915. }
  916. return nil
  917. }, false)
  918. return response, err
  919. }
  920. // Get a value through the subdoc API
  921. func (b *Bucket) GetsSubDoc(key string, reqDeadline time.Time, subPaths []string) (*gomemcached.MCResponse, error) {
  922. var err error
  923. var response *gomemcached.MCResponse
  924. if key == "" {
  925. return nil, nil
  926. }
  927. if ClientOpCallback != nil {
  928. defer func(t time.Time) { ClientOpCallback("GetsSubDoc", key, t, err) }(time.Now())
  929. }
  930. err = b.Do2(key, func(mc *memcached.Client, vb uint16) error {
  931. var err1 error
  932. mc.SetDeadline(getDeadline(reqDeadline, DefaultTimeout))
  933. response, err1 = mc.GetSubdoc(vb, key, subPaths)
  934. mc.SetDeadline(noDeadline)
  935. if err1 != nil {
  936. return err1
  937. }
  938. return nil
  939. }, false)
  940. return response, err
  941. }
  942. // GetsRaw gets a raw value from this bucket including its CAS
  943. // counter and flags.
  944. func (b *Bucket) GetsRaw(k string) (data []byte, flags int,
  945. cas uint64, err error) {
  946. if ClientOpCallback != nil {
  947. defer func(t time.Time) { ClientOpCallback("GetsRaw", k, t, err) }(time.Now())
  948. }
  949. err = b.Do(k, func(mc *memcached.Client, vb uint16) error {
  950. res, err := mc.Get(vb, k)
  951. if err != nil {
  952. return err
  953. }
  954. cas = res.Cas
  955. if len(res.Extras) >= 4 {
  956. flags = int(binary.BigEndian.Uint32(res.Extras))
  957. }
  958. data = res.Body
  959. return nil
  960. })
  961. return
  962. }
  963. // Gets gets a value from this bucket, including its CAS counter. The
  964. // value is expected to be a JSON stream and will be deserialized into
  965. // rv.
  966. func (b *Bucket) Gets(k string, rv interface{}, caso *uint64) error {
  967. data, _, cas, err := b.GetsRaw(k)
  968. if err != nil {
  969. return err
  970. }
  971. if caso != nil {
  972. *caso = cas
  973. }
  974. return json.Unmarshal(data, rv)
  975. }
  976. // Get a value from this bucket.
  977. // The value is expected to be a JSON stream and will be deserialized
  978. // into rv.
  979. func (b *Bucket) Get(k string, rv interface{}) error {
  980. return b.Gets(k, rv, nil)
  981. }
  982. // GetRaw gets a raw value from this bucket. No marshaling is performed.
  983. func (b *Bucket) GetRaw(k string) ([]byte, error) {
  984. d, _, _, err := b.GetsRaw(k)
  985. return d, err
  986. }
  987. // GetAndTouchRaw gets a raw value from this bucket including its CAS
  988. // counter and flags, and updates the expiry on the doc.
  989. func (b *Bucket) GetAndTouchRaw(k string, exp int) (data []byte,
  990. cas uint64, err error) {
  991. if ClientOpCallback != nil {
  992. defer func(t time.Time) { ClientOpCallback("GetsRaw", k, t, err) }(time.Now())
  993. }
  994. err = b.Do(k, func(mc *memcached.Client, vb uint16) error {
  995. res, err := mc.GetAndTouch(vb, k, exp)
  996. if err != nil {
  997. return err
  998. }
  999. cas = res.Cas
  1000. data = res.Body
  1001. return nil
  1002. })
  1003. return data, cas, err
  1004. }
  1005. // GetMeta returns the meta values for a key
  1006. func (b *Bucket) GetMeta(k string, flags *int, expiry *int, cas *uint64, seqNo *uint64) (err error) {
  1007. if ClientOpCallback != nil {
  1008. defer func(t time.Time) { ClientOpCallback("GetsMeta", k, t, err) }(time.Now())
  1009. }
  1010. err = b.Do(k, func(mc *memcached.Client, vb uint16) error {
  1011. res, err := mc.GetMeta(vb, k)
  1012. if err != nil {
  1013. return err
  1014. }
  1015. *cas = res.Cas
  1016. if len(res.Extras) >= 8 {
  1017. *flags = int(binary.BigEndian.Uint32(res.Extras[4:]))
  1018. }
  1019. if len(res.Extras) >= 12 {
  1020. *expiry = int(binary.BigEndian.Uint32(res.Extras[8:]))
  1021. }
  1022. if len(res.Extras) >= 20 {
  1023. *seqNo = uint64(binary.BigEndian.Uint64(res.Extras[12:]))
  1024. }
  1025. return nil
  1026. })
  1027. return err
  1028. }
  1029. // Delete a key from this bucket.
  1030. func (b *Bucket) Delete(k string) error {
  1031. return b.Write(k, 0, 0, nil, Raw)
  1032. }
  1033. // Incr increments the value at a given key by amt and defaults to def if no value present.
  1034. func (b *Bucket) Incr(k string, amt, def uint64, exp int) (val uint64, err error) {
  1035. if ClientOpCallback != nil {
  1036. defer func(t time.Time) { ClientOpCallback("Incr", k, t, err) }(time.Now())
  1037. }
  1038. var rv uint64
  1039. err = b.Do(k, func(mc *memcached.Client, vb uint16) error {
  1040. res, err := mc.Incr(vb, k, amt, def, exp)
  1041. if err != nil {
  1042. return err
  1043. }
  1044. rv = res
  1045. return nil
  1046. })
  1047. return rv, err
  1048. }
  1049. // Decr decrements the value at a given key by amt and defaults to def if no value present
  1050. func (b *Bucket) Decr(k string, amt, def uint64, exp int) (val uint64, err error) {
  1051. if ClientOpCallback != nil {
  1052. defer func(t time.Time) { ClientOpCallback("Decr", k, t, err) }(time.Now())
  1053. }
  1054. var rv uint64
  1055. err = b.Do(k, func(mc *memcached.Client, vb uint16) error {
  1056. res, err := mc.Decr(vb, k, amt, def, exp)
  1057. if err != nil {
  1058. return err
  1059. }
  1060. rv = res
  1061. return nil
  1062. })
  1063. return rv, err
  1064. }
  1065. // Wrapper around memcached.CASNext()
  1066. func (b *Bucket) casNext(k string, exp int, state *memcached.CASState) bool {
  1067. if ClientOpCallback != nil {
  1068. defer func(t time.Time) {
  1069. ClientOpCallback("casNext", k, t, state.Err)
  1070. }(time.Now())
  1071. }
  1072. keepGoing := false
  1073. state.Err = b.Do(k, func(mc *memcached.Client, vb uint16) error {
  1074. keepGoing = mc.CASNext(vb, k, exp, state)
  1075. return state.Err
  1076. })
  1077. return keepGoing && state.Err == nil
  1078. }
  1079. // An UpdateFunc is a callback function to update a document
  1080. type UpdateFunc func(current []byte) (updated []byte, err error)
  1081. // Return this as the error from an UpdateFunc to cancel the Update
  1082. // operation.
  1083. const UpdateCancel = memcached.CASQuit
  1084. // Update performs a Safe update of a document, avoiding conflicts by
  1085. // using CAS.
  1086. //
  1087. // The callback function will be invoked with the current raw document
  1088. // contents (or nil if the document doesn't exist); it should return
  1089. // the updated raw contents (or nil to delete.) If it decides not to
  1090. // change anything it can return UpdateCancel as the error.
  1091. //
  1092. // If another writer modifies the document between the get and the
  1093. // set, the callback will be invoked again with the newer value.
  1094. func (b *Bucket) Update(k string, exp int, callback UpdateFunc) error {
  1095. _, err := b.update(k, exp, callback)
  1096. return err
  1097. }
  1098. // internal version of Update that returns a CAS value
  1099. func (b *Bucket) update(k string, exp int, callback UpdateFunc) (newCas uint64, err error) {
  1100. var state memcached.CASState
  1101. for b.casNext(k, exp, &state) {
  1102. var err error
  1103. if state.Value, err = callback(state.Value); err != nil {
  1104. return 0, err
  1105. }
  1106. }
  1107. return state.Cas, state.Err
  1108. }
  1109. // A WriteUpdateFunc is a callback function to update a document
  1110. type WriteUpdateFunc func(current []byte) (updated []byte, opt WriteOptions, err error)
  1111. // WriteUpdate performs a Safe update of a document, avoiding
  1112. // conflicts by using CAS. WriteUpdate is like Update, except that
  1113. // the callback can return a set of WriteOptions, of which Persist and
  1114. // Indexable are recognized: these cause the call to wait until the
  1115. // document update has been persisted to disk and/or become available
  1116. // to index.
  1117. func (b *Bucket) WriteUpdate(k string, exp int, callback WriteUpdateFunc) error {
  1118. var writeOpts WriteOptions
  1119. var deletion bool
  1120. // Wrap the callback in an UpdateFunc we can pass to Update:
  1121. updateCallback := func(current []byte) (updated []byte, err error) {
  1122. update, opt, err := callback(current)
  1123. writeOpts = opt
  1124. deletion = (update == nil)
  1125. return update, err
  1126. }
  1127. cas, err := b.update(k, exp, updateCallback)
  1128. if err != nil {
  1129. return err
  1130. }
  1131. // If callback asked, wait for persistence or indexability:
  1132. if writeOpts&(Persist|Indexable) != 0 {
  1133. err = b.WaitForPersistence(k, cas, deletion)
  1134. }
  1135. return err
  1136. }
  1137. // Observe observes the current state of a document.
  1138. func (b *Bucket) Observe(k string) (result memcached.ObserveResult, err error) {
  1139. if ClientOpCallback != nil {
  1140. defer func(t time.Time) { ClientOpCallback("Observe", k, t, err) }(time.Now())
  1141. }
  1142. err = b.Do(k, func(mc *memcached.Client, vb uint16) error {
  1143. result, err = mc.Observe(vb, k)
  1144. return err
  1145. })
  1146. return
  1147. }
  1148. // Returned from WaitForPersistence (or Write, if the Persistent or Indexable flag is used)
  1149. // if the value has been overwritten by another before being persisted.
  1150. var ErrOverwritten = errors.New("overwritten")
  1151. // Returned from WaitForPersistence (or Write, if the Persistent or Indexable flag is used)
  1152. // if the value hasn't been persisted by the timeout interval
  1153. var ErrTimeout = errors.New("timeout")
  1154. // WaitForPersistence waits for an item to be considered durable.
  1155. //
  1156. // Besides transport errors, ErrOverwritten may be returned if the
  1157. // item is overwritten before it reaches durability. ErrTimeout may
  1158. // occur if the item isn't found durable in a reasonable amount of
  1159. // time.
  1160. func (b *Bucket) WaitForPersistence(k string, cas uint64, deletion bool) error {
  1161. timeout := 10 * time.Second
  1162. sleepDelay := 5 * time.Millisecond
  1163. start := time.Now()
  1164. for {
  1165. time.Sleep(sleepDelay)
  1166. sleepDelay += sleepDelay / 2 // multiply delay by 1.5 every time
  1167. result, err := b.Observe(k)
  1168. if err != nil {
  1169. return err
  1170. }
  1171. if persisted, overwritten := result.CheckPersistence(cas, deletion); overwritten {
  1172. return ErrOverwritten
  1173. } else if persisted {
  1174. return nil
  1175. }
  1176. if result.PersistenceTime > 0 {
  1177. timeout = 2 * result.PersistenceTime
  1178. }
  1179. if time.Since(start) >= timeout-sleepDelay {
  1180. return ErrTimeout
  1181. }
  1182. }
  1183. }
  1184. var _STRING_MCRESPONSE_POOL = gomemcached.NewStringMCResponsePool(16)
  1185. type stringPool struct {
  1186. pool *sync.Pool
  1187. size int
  1188. }
  1189. func newStringPool(size int) *stringPool {
  1190. rv := &stringPool{
  1191. pool: &sync.Pool{
  1192. New: func() interface{} {
  1193. return make([]string, 0, size)
  1194. },
  1195. },
  1196. size: size,
  1197. }
  1198. return rv
  1199. }
  1200. func (this *stringPool) Get() []string {
  1201. return this.pool.Get().([]string)
  1202. }
  1203. func (this *stringPool) Put(s []string) {
  1204. if s == nil || cap(s) < this.size || cap(s) > 2*this.size {
  1205. return
  1206. }
  1207. this.pool.Put(s[0:0])
  1208. }
  1209. var _STRING_POOL = newStringPool(16)
  1210. type vbStringPool struct {
  1211. pool *sync.Pool
  1212. strPool *stringPool
  1213. }
  1214. func newVBStringPool(size int, sp *stringPool) *vbStringPool {
  1215. rv := &vbStringPool{
  1216. pool: &sync.Pool{
  1217. New: func() interface{} {
  1218. return make(map[uint16][]string, size)
  1219. },
  1220. },
  1221. strPool: sp,
  1222. }
  1223. return rv
  1224. }
  1225. func (this *vbStringPool) Get() map[uint16][]string {
  1226. return this.pool.Get().(map[uint16][]string)
  1227. }
  1228. func (this *vbStringPool) Put(s map[uint16][]string) {
  1229. if s == nil {
  1230. return
  1231. }
  1232. for k, v := range s {
  1233. delete(s, k)
  1234. this.strPool.Put(v)
  1235. }
  1236. this.pool.Put(s)
  1237. }
  1238. var _VB_STRING_POOL = newVBStringPool(16, _STRING_POOL)