PageRenderTime 61ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/google.golang.org/appengine/memcache/memcache.go

http://github.com/garyburd/gopkgdoc
Go | 526 lines | 347 code | 43 blank | 136 comment | 79 complexity | 42be89a2003c3bc08eeed288182c5b87 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0, MPL-2.0-no-copyleft-exception, BSD-2-Clause
  1. // Copyright 2011 Google Inc. All rights reserved.
  2. // Use of this source code is governed by the Apache 2.0
  3. // license that can be found in the LICENSE file.
  4. // Package memcache provides a client for App Engine's distributed in-memory
  5. // key-value store for small chunks of arbitrary data.
  6. //
  7. // The fundamental operations get and set items, keyed by a string.
  8. //
  9. // item0, err := memcache.Get(c, "key")
  10. // if err != nil && err != memcache.ErrCacheMiss {
  11. // return err
  12. // }
  13. // if err == nil {
  14. // fmt.Fprintf(w, "memcache hit: Key=%q Val=[% x]\n", item0.Key, item0.Value)
  15. // } else {
  16. // fmt.Fprintf(w, "memcache miss\n")
  17. // }
  18. //
  19. // and
  20. //
  21. // item1 := &memcache.Item{
  22. // Key: "foo",
  23. // Value: []byte("bar"),
  24. // }
  25. // if err := memcache.Set(c, item1); err != nil {
  26. // return err
  27. // }
  28. package memcache // import "google.golang.org/appengine/memcache"
  29. import (
  30. "bytes"
  31. "encoding/gob"
  32. "encoding/json"
  33. "errors"
  34. "time"
  35. "github.com/golang/protobuf/proto"
  36. "golang.org/x/net/context"
  37. "google.golang.org/appengine"
  38. "google.golang.org/appengine/internal"
  39. pb "google.golang.org/appengine/internal/memcache"
  40. )
  41. var (
  42. // ErrCacheMiss means that an operation failed
  43. // because the item wasn't present.
  44. ErrCacheMiss = errors.New("memcache: cache miss")
  45. // ErrCASConflict means that a CompareAndSwap call failed due to the
  46. // cached value being modified between the Get and the CompareAndSwap.
  47. // If the cached value was simply evicted rather than replaced,
  48. // ErrNotStored will be returned instead.
  49. ErrCASConflict = errors.New("memcache: compare-and-swap conflict")
  50. // ErrNoStats means that no statistics were available.
  51. ErrNoStats = errors.New("memcache: no statistics available")
  52. // ErrNotStored means that a conditional write operation (i.e. Add or
  53. // CompareAndSwap) failed because the condition was not satisfied.
  54. ErrNotStored = errors.New("memcache: item not stored")
  55. // ErrServerError means that a server error occurred.
  56. ErrServerError = errors.New("memcache: server error")
  57. )
  58. // Item is the unit of memcache gets and sets.
  59. type Item struct {
  60. // Key is the Item's key (250 bytes maximum).
  61. Key string
  62. // Value is the Item's value.
  63. Value []byte
  64. // Object is the Item's value for use with a Codec.
  65. Object interface{}
  66. // Flags are server-opaque flags whose semantics are entirely up to the
  67. // App Engine app.
  68. Flags uint32
  69. // Expiration is the maximum duration that the item will stay
  70. // in the cache.
  71. // The zero value means the Item has no expiration time.
  72. // Subsecond precision is ignored.
  73. // This is not set when getting items.
  74. Expiration time.Duration
  75. // casID is a client-opaque value used for compare-and-swap operations.
  76. // Zero means that compare-and-swap is not used.
  77. casID uint64
  78. }
  79. const (
  80. secondsIn30Years = 60 * 60 * 24 * 365 * 30 // from memcache server code
  81. thirtyYears = time.Duration(secondsIn30Years) * time.Second
  82. )
  83. // protoToItem converts a protocol buffer item to a Go struct.
  84. func protoToItem(p *pb.MemcacheGetResponse_Item) *Item {
  85. return &Item{
  86. Key: string(p.Key),
  87. Value: p.Value,
  88. Flags: p.GetFlags(),
  89. casID: p.GetCasId(),
  90. }
  91. }
  92. // If err is an appengine.MultiError, return its first element. Otherwise, return err.
  93. func singleError(err error) error {
  94. if me, ok := err.(appengine.MultiError); ok {
  95. return me[0]
  96. }
  97. return err
  98. }
  99. // Get gets the item for the given key. ErrCacheMiss is returned for a memcache
  100. // cache miss. The key must be at most 250 bytes in length.
  101. func Get(c context.Context, key string) (*Item, error) {
  102. m, err := GetMulti(c, []string{key})
  103. if err != nil {
  104. return nil, err
  105. }
  106. if _, ok := m[key]; !ok {
  107. return nil, ErrCacheMiss
  108. }
  109. return m[key], nil
  110. }
  111. // GetMulti is a batch version of Get. The returned map from keys to items may
  112. // have fewer elements than the input slice, due to memcache cache misses.
  113. // Each key must be at most 250 bytes in length.
  114. func GetMulti(c context.Context, key []string) (map[string]*Item, error) {
  115. if len(key) == 0 {
  116. return nil, nil
  117. }
  118. keyAsBytes := make([][]byte, len(key))
  119. for i, k := range key {
  120. keyAsBytes[i] = []byte(k)
  121. }
  122. req := &pb.MemcacheGetRequest{
  123. Key: keyAsBytes,
  124. ForCas: proto.Bool(true),
  125. }
  126. res := &pb.MemcacheGetResponse{}
  127. if err := internal.Call(c, "memcache", "Get", req, res); err != nil {
  128. return nil, err
  129. }
  130. m := make(map[string]*Item, len(res.Item))
  131. for _, p := range res.Item {
  132. t := protoToItem(p)
  133. m[t.Key] = t
  134. }
  135. return m, nil
  136. }
  137. // Delete deletes the item for the given key.
  138. // ErrCacheMiss is returned if the specified item can not be found.
  139. // The key must be at most 250 bytes in length.
  140. func Delete(c context.Context, key string) error {
  141. return singleError(DeleteMulti(c, []string{key}))
  142. }
  143. // DeleteMulti is a batch version of Delete.
  144. // If any keys cannot be found, an appengine.MultiError is returned.
  145. // Each key must be at most 250 bytes in length.
  146. func DeleteMulti(c context.Context, key []string) error {
  147. if len(key) == 0 {
  148. return nil
  149. }
  150. req := &pb.MemcacheDeleteRequest{
  151. Item: make([]*pb.MemcacheDeleteRequest_Item, len(key)),
  152. }
  153. for i, k := range key {
  154. req.Item[i] = &pb.MemcacheDeleteRequest_Item{Key: []byte(k)}
  155. }
  156. res := &pb.MemcacheDeleteResponse{}
  157. if err := internal.Call(c, "memcache", "Delete", req, res); err != nil {
  158. return err
  159. }
  160. if len(res.DeleteStatus) != len(key) {
  161. return ErrServerError
  162. }
  163. me, any := make(appengine.MultiError, len(key)), false
  164. for i, s := range res.DeleteStatus {
  165. switch s {
  166. case pb.MemcacheDeleteResponse_DELETED:
  167. // OK
  168. case pb.MemcacheDeleteResponse_NOT_FOUND:
  169. me[i] = ErrCacheMiss
  170. any = true
  171. default:
  172. me[i] = ErrServerError
  173. any = true
  174. }
  175. }
  176. if any {
  177. return me
  178. }
  179. return nil
  180. }
  181. // Increment atomically increments the decimal value in the given key
  182. // by delta and returns the new value. The value must fit in a uint64.
  183. // Overflow wraps around, and underflow is capped to zero. The
  184. // provided delta may be negative. If the key doesn't exist in
  185. // memcache, the provided initial value is used to atomically
  186. // populate it before the delta is applied.
  187. // The key must be at most 250 bytes in length.
  188. func Increment(c context.Context, key string, delta int64, initialValue uint64) (newValue uint64, err error) {
  189. return incr(c, key, delta, &initialValue)
  190. }
  191. // IncrementExisting works like Increment but assumes that the key
  192. // already exists in memcache and doesn't take an initial value.
  193. // IncrementExisting can save work if calculating the initial value is
  194. // expensive.
  195. // An error is returned if the specified item can not be found.
  196. func IncrementExisting(c context.Context, key string, delta int64) (newValue uint64, err error) {
  197. return incr(c, key, delta, nil)
  198. }
  199. func incr(c context.Context, key string, delta int64, initialValue *uint64) (newValue uint64, err error) {
  200. req := &pb.MemcacheIncrementRequest{
  201. Key: []byte(key),
  202. InitialValue: initialValue,
  203. }
  204. if delta >= 0 {
  205. req.Delta = proto.Uint64(uint64(delta))
  206. } else {
  207. req.Delta = proto.Uint64(uint64(-delta))
  208. req.Direction = pb.MemcacheIncrementRequest_DECREMENT.Enum()
  209. }
  210. res := &pb.MemcacheIncrementResponse{}
  211. err = internal.Call(c, "memcache", "Increment", req, res)
  212. if err != nil {
  213. return
  214. }
  215. if res.NewValue == nil {
  216. return 0, ErrCacheMiss
  217. }
  218. return *res.NewValue, nil
  219. }
  220. // set sets the given items using the given conflict resolution policy.
  221. // appengine.MultiError may be returned.
  222. func set(c context.Context, item []*Item, value [][]byte, policy pb.MemcacheSetRequest_SetPolicy) error {
  223. if len(item) == 0 {
  224. return nil
  225. }
  226. req := &pb.MemcacheSetRequest{
  227. Item: make([]*pb.MemcacheSetRequest_Item, len(item)),
  228. }
  229. for i, t := range item {
  230. p := &pb.MemcacheSetRequest_Item{
  231. Key: []byte(t.Key),
  232. }
  233. if value == nil {
  234. p.Value = t.Value
  235. } else {
  236. p.Value = value[i]
  237. }
  238. if t.Flags != 0 {
  239. p.Flags = proto.Uint32(t.Flags)
  240. }
  241. if t.Expiration != 0 {
  242. // In the .proto file, MemcacheSetRequest_Item uses a fixed32 (i.e. unsigned)
  243. // for expiration time, while MemcacheGetRequest_Item uses int32 (i.e. signed).
  244. // Throughout this .go file, we use int32.
  245. // Also, in the proto, the expiration value is either a duration (in seconds)
  246. // or an absolute Unix timestamp (in seconds), depending on whether the
  247. // value is less than or greater than or equal to 30 years, respectively.
  248. if t.Expiration < time.Second {
  249. // Because an Expiration of 0 means no expiration, we take
  250. // care here to translate an item with an expiration
  251. // Duration between 0-1 seconds as immediately expiring
  252. // (saying it expired a few seconds ago), rather than
  253. // rounding it down to 0 and making it live forever.
  254. p.ExpirationTime = proto.Uint32(uint32(time.Now().Unix()) - 5)
  255. } else if t.Expiration >= thirtyYears {
  256. p.ExpirationTime = proto.Uint32(uint32(time.Now().Unix()) + uint32(t.Expiration/time.Second))
  257. } else {
  258. p.ExpirationTime = proto.Uint32(uint32(t.Expiration / time.Second))
  259. }
  260. }
  261. if t.casID != 0 {
  262. p.CasId = proto.Uint64(t.casID)
  263. p.ForCas = proto.Bool(true)
  264. }
  265. p.SetPolicy = policy.Enum()
  266. req.Item[i] = p
  267. }
  268. res := &pb.MemcacheSetResponse{}
  269. if err := internal.Call(c, "memcache", "Set", req, res); err != nil {
  270. return err
  271. }
  272. if len(res.SetStatus) != len(item) {
  273. return ErrServerError
  274. }
  275. me, any := make(appengine.MultiError, len(item)), false
  276. for i, st := range res.SetStatus {
  277. var err error
  278. switch st {
  279. case pb.MemcacheSetResponse_STORED:
  280. // OK
  281. case pb.MemcacheSetResponse_NOT_STORED:
  282. err = ErrNotStored
  283. case pb.MemcacheSetResponse_EXISTS:
  284. err = ErrCASConflict
  285. default:
  286. err = ErrServerError
  287. }
  288. if err != nil {
  289. me[i] = err
  290. any = true
  291. }
  292. }
  293. if any {
  294. return me
  295. }
  296. return nil
  297. }
  298. // Set writes the given item, unconditionally.
  299. func Set(c context.Context, item *Item) error {
  300. return singleError(set(c, []*Item{item}, nil, pb.MemcacheSetRequest_SET))
  301. }
  302. // SetMulti is a batch version of Set.
  303. // appengine.MultiError may be returned.
  304. func SetMulti(c context.Context, item []*Item) error {
  305. return set(c, item, nil, pb.MemcacheSetRequest_SET)
  306. }
  307. // Add writes the given item, if no value already exists for its key.
  308. // ErrNotStored is returned if that condition is not met.
  309. func Add(c context.Context, item *Item) error {
  310. return singleError(set(c, []*Item{item}, nil, pb.MemcacheSetRequest_ADD))
  311. }
  312. // AddMulti is a batch version of Add.
  313. // appengine.MultiError may be returned.
  314. func AddMulti(c context.Context, item []*Item) error {
  315. return set(c, item, nil, pb.MemcacheSetRequest_ADD)
  316. }
  317. // CompareAndSwap writes the given item that was previously returned by Get,
  318. // if the value was neither modified or evicted between the Get and the
  319. // CompareAndSwap calls. The item's Key should not change between calls but
  320. // all other item fields may differ.
  321. // ErrCASConflict is returned if the value was modified in between the calls.
  322. // ErrNotStored is returned if the value was evicted in between the calls.
  323. func CompareAndSwap(c context.Context, item *Item) error {
  324. return singleError(set(c, []*Item{item}, nil, pb.MemcacheSetRequest_CAS))
  325. }
  326. // CompareAndSwapMulti is a batch version of CompareAndSwap.
  327. // appengine.MultiError may be returned.
  328. func CompareAndSwapMulti(c context.Context, item []*Item) error {
  329. return set(c, item, nil, pb.MemcacheSetRequest_CAS)
  330. }
  331. // Codec represents a symmetric pair of functions that implement a codec.
  332. // Items stored into or retrieved from memcache using a Codec have their
  333. // values marshaled or unmarshaled.
  334. //
  335. // All the methods provided for Codec behave analogously to the package level
  336. // function with same name.
  337. type Codec struct {
  338. Marshal func(interface{}) ([]byte, error)
  339. Unmarshal func([]byte, interface{}) error
  340. }
  341. // Get gets the item for the given key and decodes the obtained value into v.
  342. // ErrCacheMiss is returned for a memcache cache miss.
  343. // The key must be at most 250 bytes in length.
  344. func (cd Codec) Get(c context.Context, key string, v interface{}) (*Item, error) {
  345. i, err := Get(c, key)
  346. if err != nil {
  347. return nil, err
  348. }
  349. if err := cd.Unmarshal(i.Value, v); err != nil {
  350. return nil, err
  351. }
  352. return i, nil
  353. }
  354. func (cd Codec) set(c context.Context, items []*Item, policy pb.MemcacheSetRequest_SetPolicy) error {
  355. var vs [][]byte
  356. var me appengine.MultiError
  357. for i, item := range items {
  358. v, err := cd.Marshal(item.Object)
  359. if err != nil {
  360. if me == nil {
  361. me = make(appengine.MultiError, len(items))
  362. }
  363. me[i] = err
  364. continue
  365. }
  366. if me == nil {
  367. vs = append(vs, v)
  368. }
  369. }
  370. if me != nil {
  371. return me
  372. }
  373. return set(c, items, vs, policy)
  374. }
  375. // Set writes the given item, unconditionally.
  376. func (cd Codec) Set(c context.Context, item *Item) error {
  377. return singleError(cd.set(c, []*Item{item}, pb.MemcacheSetRequest_SET))
  378. }
  379. // SetMulti is a batch version of Set.
  380. // appengine.MultiError may be returned.
  381. func (cd Codec) SetMulti(c context.Context, items []*Item) error {
  382. return cd.set(c, items, pb.MemcacheSetRequest_SET)
  383. }
  384. // Add writes the given item, if no value already exists for its key.
  385. // ErrNotStored is returned if that condition is not met.
  386. func (cd Codec) Add(c context.Context, item *Item) error {
  387. return singleError(cd.set(c, []*Item{item}, pb.MemcacheSetRequest_ADD))
  388. }
  389. // AddMulti is a batch version of Add.
  390. // appengine.MultiError may be returned.
  391. func (cd Codec) AddMulti(c context.Context, items []*Item) error {
  392. return cd.set(c, items, pb.MemcacheSetRequest_ADD)
  393. }
  394. // CompareAndSwap writes the given item that was previously returned by Get,
  395. // if the value was neither modified or evicted between the Get and the
  396. // CompareAndSwap calls. The item's Key should not change between calls but
  397. // all other item fields may differ.
  398. // ErrCASConflict is returned if the value was modified in between the calls.
  399. // ErrNotStored is returned if the value was evicted in between the calls.
  400. func (cd Codec) CompareAndSwap(c context.Context, item *Item) error {
  401. return singleError(cd.set(c, []*Item{item}, pb.MemcacheSetRequest_CAS))
  402. }
  403. // CompareAndSwapMulti is a batch version of CompareAndSwap.
  404. // appengine.MultiError may be returned.
  405. func (cd Codec) CompareAndSwapMulti(c context.Context, items []*Item) error {
  406. return cd.set(c, items, pb.MemcacheSetRequest_CAS)
  407. }
  408. var (
  409. // Gob is a Codec that uses the gob package.
  410. Gob = Codec{gobMarshal, gobUnmarshal}
  411. // JSON is a Codec that uses the json package.
  412. JSON = Codec{json.Marshal, json.Unmarshal}
  413. )
  414. func gobMarshal(v interface{}) ([]byte, error) {
  415. var buf bytes.Buffer
  416. if err := gob.NewEncoder(&buf).Encode(v); err != nil {
  417. return nil, err
  418. }
  419. return buf.Bytes(), nil
  420. }
  421. func gobUnmarshal(data []byte, v interface{}) error {
  422. return gob.NewDecoder(bytes.NewBuffer(data)).Decode(v)
  423. }
  424. // Statistics represents a set of statistics about the memcache cache.
  425. // This may include items that have expired but have not yet been removed from the cache.
  426. type Statistics struct {
  427. Hits uint64 // Counter of cache hits
  428. Misses uint64 // Counter of cache misses
  429. ByteHits uint64 // Counter of bytes transferred for gets
  430. Items uint64 // Items currently in the cache
  431. Bytes uint64 // Size of all items currently in the cache
  432. Oldest int64 // Age of access of the oldest item, in seconds
  433. }
  434. // Stats retrieves the current memcache statistics.
  435. func Stats(c context.Context) (*Statistics, error) {
  436. req := &pb.MemcacheStatsRequest{}
  437. res := &pb.MemcacheStatsResponse{}
  438. if err := internal.Call(c, "memcache", "Stats", req, res); err != nil {
  439. return nil, err
  440. }
  441. if res.Stats == nil {
  442. return nil, ErrNoStats
  443. }
  444. return &Statistics{
  445. Hits: *res.Stats.Hits,
  446. Misses: *res.Stats.Misses,
  447. ByteHits: *res.Stats.ByteHits,
  448. Items: *res.Stats.Items,
  449. Bytes: *res.Stats.Bytes,
  450. Oldest: int64(*res.Stats.OldestItemAge),
  451. }, nil
  452. }
  453. // Flush flushes all items from memcache.
  454. func Flush(c context.Context) error {
  455. req := &pb.MemcacheFlushRequest{}
  456. res := &pb.MemcacheFlushResponse{}
  457. return internal.Call(c, "memcache", "FlushAll", req, res)
  458. }
  459. func namespaceMod(m proto.Message, namespace string) {
  460. switch m := m.(type) {
  461. case *pb.MemcacheDeleteRequest:
  462. if m.NameSpace == nil {
  463. m.NameSpace = &namespace
  464. }
  465. case *pb.MemcacheGetRequest:
  466. if m.NameSpace == nil {
  467. m.NameSpace = &namespace
  468. }
  469. case *pb.MemcacheIncrementRequest:
  470. if m.NameSpace == nil {
  471. m.NameSpace = &namespace
  472. }
  473. case *pb.MemcacheSetRequest:
  474. if m.NameSpace == nil {
  475. m.NameSpace = &namespace
  476. }
  477. // MemcacheFlushRequest, MemcacheStatsRequest do not apply namespace.
  478. }
  479. }
  480. func init() {
  481. internal.RegisterErrorCodeMap("memcache", pb.MemcacheServiceError_ErrorCode_name)
  482. internal.NamespaceMods["memcache"] = namespaceMod
  483. }