appengine/v2/memcache/memcache.go

590 строки
18 KiB
Go
Исходник Обычный вид История

Merge QA into the main branch (#247) * Removing old, unsupported APIs (#230) * Remove the old Search API. * Remove the old Remote API. * Remove the old sockets API. * Delete legacy appengine code (#231) - Delete all files behind `+build appengine` - Merge the `*_vm.go` variants with the base files - `go fix` for context update (TODO: Fix internal/api.go's use of context) - Update travis config to test new versions * Remove Query APIs from appengine.log (#233) Remove deprecated log.Query api from appengine.log * Update docs (#234) * Remove the old Search API. * Remove the old Remote API. * Remove the old sockets API. * Temporarily remove blobstore API for v2 preview. * Revert "Temporarily remove blobstore API for v2 preview." This reverts commit 169bbdd4674a0fc862fd6a12a843eb2b3bba01cd. * Update docs for QA. * Update module API docs to indicate manual_scaling must be used for NumInstances, etc. * Update module API docs to indicate manual_scaling must be used for NumInstances, etc. * Remove old interfaces and interfaces that are not recommended (#236) * Remove the old deprecated channel interface. * Remove the old deprecated file interface. * Remove the old deprecated xmpp interface. * Removing the cloudsql interface, which is not recommended. * Removing the aebundler interface, which has been deprecated for some time. * Remove obsolete demo code (#238) * Remove obsolete demo code. Official appengine sample code referred by doc is in https://github.com/GoogleCloudPlatform/golang-samples * Replace Logs API with Stdout (#240) * Removed Logs API. * Remove dead code. * Added JSON encoding tests. * Emit single character severity in JSON message to trigger structured logs handling. * Prevent timestamp from being emitted on deployed logs. * Remove superfluous apis (#241) * Removed protos for channel/search/xmpp. * Remove protos for socket. * Add traceID/spanID to structured logs and chunk long logs (#242) * Moved logging related logic to separate file. * Added traceID/spanID to structured logs and log chunking. * Changes from code review. * Update go.mod for v2 * Revert v2 in the base directory. * Create the v2 in /v2 and revert the base directory back to 1.6.7 (#243) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy v2/go.mod (#244) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy go.sum * go mod tidy (#245) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy go.sum * go mod tidy Co-authored-by: Steven Buss <sbuss@google.com> Co-authored-by: Kayla Nguyen <kaylanguyen@google.com> Co-authored-by: Jason Collins <jason.a.collins@gmail.com>
2021-06-15 20:57:49 +03:00
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
// Package memcache provides a client for App Engine's distributed in-memory
// key-value store for small chunks of arbitrary data.
//
// The fundamental operations get and set items, keyed by a string.
//
// item0, err := memcache.Get(c, "key")
// if err != nil && err != memcache.ErrCacheMiss {
// return err
// }
// if err == nil {
// fmt.Fprintf(w, "memcache hit: Key=%q Val=[% x]\n", item0.Key, item0.Value)
// } else {
// fmt.Fprintf(w, "memcache miss\n")
// }
//
// and
//
// item1 := &memcache.Item{
// Key: "foo",
// Value: []byte("bar"),
// }
// if err := memcache.Set(c, item1); err != nil {
// return err
// }
package memcache // import "google.golang.org/appengine/v2/memcache"
import (
"bytes"
"context"
"encoding/gob"
"encoding/json"
"errors"
"time"
"github.com/golang/protobuf/proto"
"google.golang.org/appengine/v2"
"google.golang.org/appengine/v2/internal"
pb "google.golang.org/appengine/v2/internal/memcache"
)
var (
// ErrCacheMiss means that an operation failed
// because the item wasn't present.
ErrCacheMiss = errors.New("memcache: cache miss")
// ErrCASConflict means that a CompareAndSwap call failed due to the
// cached value being modified between the Get and the CompareAndSwap.
// If the cached value was simply evicted rather than replaced,
// ErrNotStored will be returned instead.
ErrCASConflict = errors.New("memcache: compare-and-swap conflict")
// ErrNoStats means that no statistics were available.
ErrNoStats = errors.New("memcache: no statistics available")
// ErrNotStored means that a conditional write operation (i.e. Add or
// CompareAndSwap) failed because the condition was not satisfied.
ErrNotStored = errors.New("memcache: item not stored")
// ErrServerError means that a server error occurred.
ErrServerError = errors.New("memcache: server error")
)
// Item is the unit of memcache gets and sets.
type Item struct {
// Key is the Item's key (250 bytes maximum).
Key string
// Value is the Item's value.
Value []byte
// Object is the Item's value for use with a Codec.
Object interface{}
// Flags are server-opaque flags whose semantics are entirely up to the
// App Engine app.
Flags uint32
// Expiration is the maximum duration that the item will stay
// in the cache.
// The zero value means the Item has no expiration time.
// Subsecond precision is ignored.
// This is not set when getting items.
Expiration time.Duration
// casID is a client-opaque value used for compare-and-swap operations.
// Zero means that compare-and-swap is not used.
casID uint64
// ItemTimestamps are server values only returned when calling Peek and PeekMulti.
// The timestamps are nil when calling Get and GetMulti.
Timestamps ItemTimestamps
}
// ItemTimestamps are timestamps optionally provided by the server.
// See Peek and PeekMulti.
type ItemTimestamps struct {
// Expiration is related to Item.Expiration but it is a Time (not a Duration),
// provided by the server. It is not meant to be set by the user.
Expiration *time.Time
// LastAccess is the last time the Item was accessed.
LastAccess *time.Time
// The proto also includes delete_lock_time_sec, which is ignored in the Go lib.
Merge QA into the main branch (#247) * Removing old, unsupported APIs (#230) * Remove the old Search API. * Remove the old Remote API. * Remove the old sockets API. * Delete legacy appengine code (#231) - Delete all files behind `+build appengine` - Merge the `*_vm.go` variants with the base files - `go fix` for context update (TODO: Fix internal/api.go's use of context) - Update travis config to test new versions * Remove Query APIs from appengine.log (#233) Remove deprecated log.Query api from appengine.log * Update docs (#234) * Remove the old Search API. * Remove the old Remote API. * Remove the old sockets API. * Temporarily remove blobstore API for v2 preview. * Revert "Temporarily remove blobstore API for v2 preview." This reverts commit 169bbdd4674a0fc862fd6a12a843eb2b3bba01cd. * Update docs for QA. * Update module API docs to indicate manual_scaling must be used for NumInstances, etc. * Update module API docs to indicate manual_scaling must be used for NumInstances, etc. * Remove old interfaces and interfaces that are not recommended (#236) * Remove the old deprecated channel interface. * Remove the old deprecated file interface. * Remove the old deprecated xmpp interface. * Removing the cloudsql interface, which is not recommended. * Removing the aebundler interface, which has been deprecated for some time. * Remove obsolete demo code (#238) * Remove obsolete demo code. Official appengine sample code referred by doc is in https://github.com/GoogleCloudPlatform/golang-samples * Replace Logs API with Stdout (#240) * Removed Logs API. * Remove dead code. * Added JSON encoding tests. * Emit single character severity in JSON message to trigger structured logs handling. * Prevent timestamp from being emitted on deployed logs. * Remove superfluous apis (#241) * Removed protos for channel/search/xmpp. * Remove protos for socket. * Add traceID/spanID to structured logs and chunk long logs (#242) * Moved logging related logic to separate file. * Added traceID/spanID to structured logs and log chunking. * Changes from code review. * Update go.mod for v2 * Revert v2 in the base directory. * Create the v2 in /v2 and revert the base directory back to 1.6.7 (#243) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy v2/go.mod (#244) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy go.sum * go mod tidy (#245) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy go.sum * go mod tidy Co-authored-by: Steven Buss <sbuss@google.com> Co-authored-by: Kayla Nguyen <kaylanguyen@google.com> Co-authored-by: Jason Collins <jason.a.collins@gmail.com>
2021-06-15 20:57:49 +03:00
}
const (
secondsIn30Years = 60 * 60 * 24 * 365 * 30 // from memcache server code
thirtyYears = time.Duration(secondsIn30Years) * time.Second
)
// protoToItem converts a protocol buffer item to a Go struct.
func protoToItem(p *pb.MemcacheGetResponse_Item) *Item {
if p.GetIsDeleteLocked() {
// "delete lock" for a duration is not a feature available in the Go lib.
// Such items may exist in memcache though, e.g. created by the Java lib.
// In this case, nil is more appropriate than an item with empty value.
// For a single Get, nil will translate to ErrCacheMiss.
return nil
}
Merge QA into the main branch (#247) * Removing old, unsupported APIs (#230) * Remove the old Search API. * Remove the old Remote API. * Remove the old sockets API. * Delete legacy appengine code (#231) - Delete all files behind `+build appengine` - Merge the `*_vm.go` variants with the base files - `go fix` for context update (TODO: Fix internal/api.go's use of context) - Update travis config to test new versions * Remove Query APIs from appengine.log (#233) Remove deprecated log.Query api from appengine.log * Update docs (#234) * Remove the old Search API. * Remove the old Remote API. * Remove the old sockets API. * Temporarily remove blobstore API for v2 preview. * Revert "Temporarily remove blobstore API for v2 preview." This reverts commit 169bbdd4674a0fc862fd6a12a843eb2b3bba01cd. * Update docs for QA. * Update module API docs to indicate manual_scaling must be used for NumInstances, etc. * Update module API docs to indicate manual_scaling must be used for NumInstances, etc. * Remove old interfaces and interfaces that are not recommended (#236) * Remove the old deprecated channel interface. * Remove the old deprecated file interface. * Remove the old deprecated xmpp interface. * Removing the cloudsql interface, which is not recommended. * Removing the aebundler interface, which has been deprecated for some time. * Remove obsolete demo code (#238) * Remove obsolete demo code. Official appengine sample code referred by doc is in https://github.com/GoogleCloudPlatform/golang-samples * Replace Logs API with Stdout (#240) * Removed Logs API. * Remove dead code. * Added JSON encoding tests. * Emit single character severity in JSON message to trigger structured logs handling. * Prevent timestamp from being emitted on deployed logs. * Remove superfluous apis (#241) * Removed protos for channel/search/xmpp. * Remove protos for socket. * Add traceID/spanID to structured logs and chunk long logs (#242) * Moved logging related logic to separate file. * Added traceID/spanID to structured logs and log chunking. * Changes from code review. * Update go.mod for v2 * Revert v2 in the base directory. * Create the v2 in /v2 and revert the base directory back to 1.6.7 (#243) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy v2/go.mod (#244) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy go.sum * go mod tidy (#245) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy go.sum * go mod tidy Co-authored-by: Steven Buss <sbuss@google.com> Co-authored-by: Kayla Nguyen <kaylanguyen@google.com> Co-authored-by: Jason Collins <jason.a.collins@gmail.com>
2021-06-15 20:57:49 +03:00
return &Item{
Key: string(p.Key),
Value: p.Value,
Flags: p.GetFlags(),
casID: p.GetCasId(),
Timestamps: ItemTimestamps{
Expiration: timeSecToTime(p.GetTimestamps().GetExpirationTimeSec()),
LastAccess: timeSecToTime(p.GetTimestamps().GetLastAccessTimeSec()),
},
Merge QA into the main branch (#247) * Removing old, unsupported APIs (#230) * Remove the old Search API. * Remove the old Remote API. * Remove the old sockets API. * Delete legacy appengine code (#231) - Delete all files behind `+build appengine` - Merge the `*_vm.go` variants with the base files - `go fix` for context update (TODO: Fix internal/api.go's use of context) - Update travis config to test new versions * Remove Query APIs from appengine.log (#233) Remove deprecated log.Query api from appengine.log * Update docs (#234) * Remove the old Search API. * Remove the old Remote API. * Remove the old sockets API. * Temporarily remove blobstore API for v2 preview. * Revert "Temporarily remove blobstore API for v2 preview." This reverts commit 169bbdd4674a0fc862fd6a12a843eb2b3bba01cd. * Update docs for QA. * Update module API docs to indicate manual_scaling must be used for NumInstances, etc. * Update module API docs to indicate manual_scaling must be used for NumInstances, etc. * Remove old interfaces and interfaces that are not recommended (#236) * Remove the old deprecated channel interface. * Remove the old deprecated file interface. * Remove the old deprecated xmpp interface. * Removing the cloudsql interface, which is not recommended. * Removing the aebundler interface, which has been deprecated for some time. * Remove obsolete demo code (#238) * Remove obsolete demo code. Official appengine sample code referred by doc is in https://github.com/GoogleCloudPlatform/golang-samples * Replace Logs API with Stdout (#240) * Removed Logs API. * Remove dead code. * Added JSON encoding tests. * Emit single character severity in JSON message to trigger structured logs handling. * Prevent timestamp from being emitted on deployed logs. * Remove superfluous apis (#241) * Removed protos for channel/search/xmpp. * Remove protos for socket. * Add traceID/spanID to structured logs and chunk long logs (#242) * Moved logging related logic to separate file. * Added traceID/spanID to structured logs and log chunking. * Changes from code review. * Update go.mod for v2 * Revert v2 in the base directory. * Create the v2 in /v2 and revert the base directory back to 1.6.7 (#243) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy v2/go.mod (#244) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy go.sum * go mod tidy (#245) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy go.sum * go mod tidy Co-authored-by: Steven Buss <sbuss@google.com> Co-authored-by: Kayla Nguyen <kaylanguyen@google.com> Co-authored-by: Jason Collins <jason.a.collins@gmail.com>
2021-06-15 20:57:49 +03:00
}
}
// For convenience, we interpret a 0 unix second timestamp as a nil *Time.
func timeSecToTime(s int64) *time.Time {
if s == 0 {
return nil
}
t := time.Unix(s, 0)
return &t
}
Merge QA into the main branch (#247) * Removing old, unsupported APIs (#230) * Remove the old Search API. * Remove the old Remote API. * Remove the old sockets API. * Delete legacy appengine code (#231) - Delete all files behind `+build appengine` - Merge the `*_vm.go` variants with the base files - `go fix` for context update (TODO: Fix internal/api.go's use of context) - Update travis config to test new versions * Remove Query APIs from appengine.log (#233) Remove deprecated log.Query api from appengine.log * Update docs (#234) * Remove the old Search API. * Remove the old Remote API. * Remove the old sockets API. * Temporarily remove blobstore API for v2 preview. * Revert "Temporarily remove blobstore API for v2 preview." This reverts commit 169bbdd4674a0fc862fd6a12a843eb2b3bba01cd. * Update docs for QA. * Update module API docs to indicate manual_scaling must be used for NumInstances, etc. * Update module API docs to indicate manual_scaling must be used for NumInstances, etc. * Remove old interfaces and interfaces that are not recommended (#236) * Remove the old deprecated channel interface. * Remove the old deprecated file interface. * Remove the old deprecated xmpp interface. * Removing the cloudsql interface, which is not recommended. * Removing the aebundler interface, which has been deprecated for some time. * Remove obsolete demo code (#238) * Remove obsolete demo code. Official appengine sample code referred by doc is in https://github.com/GoogleCloudPlatform/golang-samples * Replace Logs API with Stdout (#240) * Removed Logs API. * Remove dead code. * Added JSON encoding tests. * Emit single character severity in JSON message to trigger structured logs handling. * Prevent timestamp from being emitted on deployed logs. * Remove superfluous apis (#241) * Removed protos for channel/search/xmpp. * Remove protos for socket. * Add traceID/spanID to structured logs and chunk long logs (#242) * Moved logging related logic to separate file. * Added traceID/spanID to structured logs and log chunking. * Changes from code review. * Update go.mod for v2 * Revert v2 in the base directory. * Create the v2 in /v2 and revert the base directory back to 1.6.7 (#243) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy v2/go.mod (#244) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy go.sum * go mod tidy (#245) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy go.sum * go mod tidy Co-authored-by: Steven Buss <sbuss@google.com> Co-authored-by: Kayla Nguyen <kaylanguyen@google.com> Co-authored-by: Jason Collins <jason.a.collins@gmail.com>
2021-06-15 20:57:49 +03:00
// If err is an appengine.MultiError, return its first element. Otherwise, return err.
func singleError(err error) error {
if me, ok := err.(appengine.MultiError); ok {
return me[0]
}
return err
}
// Get gets the item for the given key. ErrCacheMiss is returned for a memcache
// cache miss. The key must be at most 250 bytes in length.
func Get(c context.Context, key string) (*Item, error) {
cas, peek := true, false
return get(c, key, cas, peek)
}
// GetMulti is a batch version of Get. The returned map from keys to items may
// have fewer elements than the input slice, due to memcache cache misses.
// Each key must be at most 250 bytes in length.
func GetMulti(c context.Context, key []string) (map[string]*Item, error) {
cas, peek := true, false
return getMulti(c, key, cas, peek)
}
// Peek gets the item for the given key and additionally populates Item.Timestamps.
// ErrCacheMiss is returned for a memcache cache miss. The key must be at most 250
// bytes in length.
func Peek(c context.Context, key string) (*Item, error) {
cas, peek := true, true
return get(c, key, cas, peek)
}
// PeekMulti is a batch version of Peek. It is similar to GetMulti but
// additionally populates Item.Timestamps.
func PeekMulti(c context.Context, key []string) (map[string]*Item, error) {
cas, peek := true, true
return getMulti(c, key, cas, peek)
}
func get(c context.Context, key string, forCas bool, forPeek bool) (*Item, error) {
m, err := getMulti(c, []string{key}, forCas, forPeek)
Merge QA into the main branch (#247) * Removing old, unsupported APIs (#230) * Remove the old Search API. * Remove the old Remote API. * Remove the old sockets API. * Delete legacy appengine code (#231) - Delete all files behind `+build appengine` - Merge the `*_vm.go` variants with the base files - `go fix` for context update (TODO: Fix internal/api.go's use of context) - Update travis config to test new versions * Remove Query APIs from appengine.log (#233) Remove deprecated log.Query api from appengine.log * Update docs (#234) * Remove the old Search API. * Remove the old Remote API. * Remove the old sockets API. * Temporarily remove blobstore API for v2 preview. * Revert "Temporarily remove blobstore API for v2 preview." This reverts commit 169bbdd4674a0fc862fd6a12a843eb2b3bba01cd. * Update docs for QA. * Update module API docs to indicate manual_scaling must be used for NumInstances, etc. * Update module API docs to indicate manual_scaling must be used for NumInstances, etc. * Remove old interfaces and interfaces that are not recommended (#236) * Remove the old deprecated channel interface. * Remove the old deprecated file interface. * Remove the old deprecated xmpp interface. * Removing the cloudsql interface, which is not recommended. * Removing the aebundler interface, which has been deprecated for some time. * Remove obsolete demo code (#238) * Remove obsolete demo code. Official appengine sample code referred by doc is in https://github.com/GoogleCloudPlatform/golang-samples * Replace Logs API with Stdout (#240) * Removed Logs API. * Remove dead code. * Added JSON encoding tests. * Emit single character severity in JSON message to trigger structured logs handling. * Prevent timestamp from being emitted on deployed logs. * Remove superfluous apis (#241) * Removed protos for channel/search/xmpp. * Remove protos for socket. * Add traceID/spanID to structured logs and chunk long logs (#242) * Moved logging related logic to separate file. * Added traceID/spanID to structured logs and log chunking. * Changes from code review. * Update go.mod for v2 * Revert v2 in the base directory. * Create the v2 in /v2 and revert the base directory back to 1.6.7 (#243) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy v2/go.mod (#244) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy go.sum * go mod tidy (#245) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy go.sum * go mod tidy Co-authored-by: Steven Buss <sbuss@google.com> Co-authored-by: Kayla Nguyen <kaylanguyen@google.com> Co-authored-by: Jason Collins <jason.a.collins@gmail.com>
2021-06-15 20:57:49 +03:00
if err != nil {
return nil, err
}
if _, ok := m[key]; !ok {
return nil, ErrCacheMiss
}
return m[key], nil
}
func getMulti(c context.Context, key []string, forCas bool, forPeek bool) (map[string]*Item, error) {
Merge QA into the main branch (#247) * Removing old, unsupported APIs (#230) * Remove the old Search API. * Remove the old Remote API. * Remove the old sockets API. * Delete legacy appengine code (#231) - Delete all files behind `+build appengine` - Merge the `*_vm.go` variants with the base files - `go fix` for context update (TODO: Fix internal/api.go's use of context) - Update travis config to test new versions * Remove Query APIs from appengine.log (#233) Remove deprecated log.Query api from appengine.log * Update docs (#234) * Remove the old Search API. * Remove the old Remote API. * Remove the old sockets API. * Temporarily remove blobstore API for v2 preview. * Revert "Temporarily remove blobstore API for v2 preview." This reverts commit 169bbdd4674a0fc862fd6a12a843eb2b3bba01cd. * Update docs for QA. * Update module API docs to indicate manual_scaling must be used for NumInstances, etc. * Update module API docs to indicate manual_scaling must be used for NumInstances, etc. * Remove old interfaces and interfaces that are not recommended (#236) * Remove the old deprecated channel interface. * Remove the old deprecated file interface. * Remove the old deprecated xmpp interface. * Removing the cloudsql interface, which is not recommended. * Removing the aebundler interface, which has been deprecated for some time. * Remove obsolete demo code (#238) * Remove obsolete demo code. Official appengine sample code referred by doc is in https://github.com/GoogleCloudPlatform/golang-samples * Replace Logs API with Stdout (#240) * Removed Logs API. * Remove dead code. * Added JSON encoding tests. * Emit single character severity in JSON message to trigger structured logs handling. * Prevent timestamp from being emitted on deployed logs. * Remove superfluous apis (#241) * Removed protos for channel/search/xmpp. * Remove protos for socket. * Add traceID/spanID to structured logs and chunk long logs (#242) * Moved logging related logic to separate file. * Added traceID/spanID to structured logs and log chunking. * Changes from code review. * Update go.mod for v2 * Revert v2 in the base directory. * Create the v2 in /v2 and revert the base directory back to 1.6.7 (#243) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy v2/go.mod (#244) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy go.sum * go mod tidy (#245) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy go.sum * go mod tidy Co-authored-by: Steven Buss <sbuss@google.com> Co-authored-by: Kayla Nguyen <kaylanguyen@google.com> Co-authored-by: Jason Collins <jason.a.collins@gmail.com>
2021-06-15 20:57:49 +03:00
if len(key) == 0 {
return nil, nil
}
keyAsBytes := make([][]byte, len(key))
for i, k := range key {
keyAsBytes[i] = []byte(k)
}
req := &pb.MemcacheGetRequest{
Key: keyAsBytes,
ForCas: proto.Bool(forCas),
ForPeek: proto.Bool(forPeek),
Merge QA into the main branch (#247) * Removing old, unsupported APIs (#230) * Remove the old Search API. * Remove the old Remote API. * Remove the old sockets API. * Delete legacy appengine code (#231) - Delete all files behind `+build appengine` - Merge the `*_vm.go` variants with the base files - `go fix` for context update (TODO: Fix internal/api.go's use of context) - Update travis config to test new versions * Remove Query APIs from appengine.log (#233) Remove deprecated log.Query api from appengine.log * Update docs (#234) * Remove the old Search API. * Remove the old Remote API. * Remove the old sockets API. * Temporarily remove blobstore API for v2 preview. * Revert "Temporarily remove blobstore API for v2 preview." This reverts commit 169bbdd4674a0fc862fd6a12a843eb2b3bba01cd. * Update docs for QA. * Update module API docs to indicate manual_scaling must be used for NumInstances, etc. * Update module API docs to indicate manual_scaling must be used for NumInstances, etc. * Remove old interfaces and interfaces that are not recommended (#236) * Remove the old deprecated channel interface. * Remove the old deprecated file interface. * Remove the old deprecated xmpp interface. * Removing the cloudsql interface, which is not recommended. * Removing the aebundler interface, which has been deprecated for some time. * Remove obsolete demo code (#238) * Remove obsolete demo code. Official appengine sample code referred by doc is in https://github.com/GoogleCloudPlatform/golang-samples * Replace Logs API with Stdout (#240) * Removed Logs API. * Remove dead code. * Added JSON encoding tests. * Emit single character severity in JSON message to trigger structured logs handling. * Prevent timestamp from being emitted on deployed logs. * Remove superfluous apis (#241) * Removed protos for channel/search/xmpp. * Remove protos for socket. * Add traceID/spanID to structured logs and chunk long logs (#242) * Moved logging related logic to separate file. * Added traceID/spanID to structured logs and log chunking. * Changes from code review. * Update go.mod for v2 * Revert v2 in the base directory. * Create the v2 in /v2 and revert the base directory back to 1.6.7 (#243) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy v2/go.mod (#244) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy go.sum * go mod tidy (#245) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy go.sum * go mod tidy Co-authored-by: Steven Buss <sbuss@google.com> Co-authored-by: Kayla Nguyen <kaylanguyen@google.com> Co-authored-by: Jason Collins <jason.a.collins@gmail.com>
2021-06-15 20:57:49 +03:00
}
res := &pb.MemcacheGetResponse{}
if err := internal.Call(c, "memcache", "Get", req, res); err != nil {
return nil, err
}
m := make(map[string]*Item, len(res.Item))
for _, p := range res.Item {
t := protoToItem(p)
if t != nil {
m[t.Key] = t
}
Merge QA into the main branch (#247) * Removing old, unsupported APIs (#230) * Remove the old Search API. * Remove the old Remote API. * Remove the old sockets API. * Delete legacy appengine code (#231) - Delete all files behind `+build appengine` - Merge the `*_vm.go` variants with the base files - `go fix` for context update (TODO: Fix internal/api.go's use of context) - Update travis config to test new versions * Remove Query APIs from appengine.log (#233) Remove deprecated log.Query api from appengine.log * Update docs (#234) * Remove the old Search API. * Remove the old Remote API. * Remove the old sockets API. * Temporarily remove blobstore API for v2 preview. * Revert "Temporarily remove blobstore API for v2 preview." This reverts commit 169bbdd4674a0fc862fd6a12a843eb2b3bba01cd. * Update docs for QA. * Update module API docs to indicate manual_scaling must be used for NumInstances, etc. * Update module API docs to indicate manual_scaling must be used for NumInstances, etc. * Remove old interfaces and interfaces that are not recommended (#236) * Remove the old deprecated channel interface. * Remove the old deprecated file interface. * Remove the old deprecated xmpp interface. * Removing the cloudsql interface, which is not recommended. * Removing the aebundler interface, which has been deprecated for some time. * Remove obsolete demo code (#238) * Remove obsolete demo code. Official appengine sample code referred by doc is in https://github.com/GoogleCloudPlatform/golang-samples * Replace Logs API with Stdout (#240) * Removed Logs API. * Remove dead code. * Added JSON encoding tests. * Emit single character severity in JSON message to trigger structured logs handling. * Prevent timestamp from being emitted on deployed logs. * Remove superfluous apis (#241) * Removed protos for channel/search/xmpp. * Remove protos for socket. * Add traceID/spanID to structured logs and chunk long logs (#242) * Moved logging related logic to separate file. * Added traceID/spanID to structured logs and log chunking. * Changes from code review. * Update go.mod for v2 * Revert v2 in the base directory. * Create the v2 in /v2 and revert the base directory back to 1.6.7 (#243) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy v2/go.mod (#244) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy go.sum * go mod tidy (#245) * Create a v2 directory for v2+ releases. * Update AppEngine imports to v2. * Update AppEngine imports to v2. * Clean up travis. We only currently support go 1.11+ * Temporarily disable testing go111+ with gopath until we can tag v2 properly. * Tidy go.sum * go mod tidy Co-authored-by: Steven Buss <sbuss@google.com> Co-authored-by: Kayla Nguyen <kaylanguyen@google.com> Co-authored-by: Jason Collins <jason.a.collins@gmail.com>
2021-06-15 20:57:49 +03:00
}
return m, nil
}
// Delete deletes the item for the given key.
// ErrCacheMiss is returned if the specified item can not be found.
// The key must be at most 250 bytes in length.
func Delete(c context.Context, key string) error {
return singleError(DeleteMulti(c, []string{key}))
}
// DeleteMulti is a batch version of Delete.
// If any keys cannot be found, an appengine.MultiError is returned.
// Each key must be at most 250 bytes in length.
func DeleteMulti(c context.Context, key []string) error {
if len(key) == 0 {
return nil
}
req := &pb.MemcacheDeleteRequest{
Item: make([]*pb.MemcacheDeleteRequest_Item, len(key)),
}
for i, k := range key {
req.Item[i] = &pb.MemcacheDeleteRequest_Item{Key: []byte(k)}
}
res := &pb.MemcacheDeleteResponse{}
if err := internal.Call(c, "memcache", "Delete", req, res); err != nil {
return err
}
if len(res.DeleteStatus) != len(key) {
return ErrServerError
}
me, any := make(appengine.MultiError, len(key)), false
for i, s := range res.DeleteStatus {
switch s {
case pb.MemcacheDeleteResponse_DELETED:
// OK
case pb.MemcacheDeleteResponse_NOT_FOUND:
me[i] = ErrCacheMiss
any = true
default:
me[i] = ErrServerError
any = true
}
}
if any {
return me
}
return nil
}
// Increment atomically increments the decimal value in the given key
// by delta and returns the new value. The value must fit in a uint64.
// Overflow wraps around, and underflow is capped to zero. The
// provided delta may be negative. If the key doesn't exist in
// memcache, the provided initial value is used to atomically
// populate it before the delta is applied.
// The key must be at most 250 bytes in length.
func Increment(c context.Context, key string, delta int64, initialValue uint64) (newValue uint64, err error) {
return incr(c, key, delta, &initialValue)
}
// IncrementExisting works like Increment but assumes that the key
// already exists in memcache and doesn't take an initial value.
// IncrementExisting can save work if calculating the initial value is
// expensive.
// An error is returned if the specified item can not be found.
func IncrementExisting(c context.Context, key string, delta int64) (newValue uint64, err error) {
return incr(c, key, delta, nil)
}
func incr(c context.Context, key string, delta int64, initialValue *uint64) (newValue uint64, err error) {
req := &pb.MemcacheIncrementRequest{
Key: []byte(key),
InitialValue: initialValue,
}
if delta >= 0 {
req.Delta = proto.Uint64(uint64(delta))
} else {
req.Delta = proto.Uint64(uint64(-delta))
req.Direction = pb.MemcacheIncrementRequest_DECREMENT.Enum()
}
res := &pb.MemcacheIncrementResponse{}
err = internal.Call(c, "memcache", "Increment", req, res)
if err != nil {
return
}
if res.NewValue == nil {
return 0, ErrCacheMiss
}
return *res.NewValue, nil
}
// set sets the given items using the given conflict resolution policy.
// appengine.MultiError may be returned.
func set(c context.Context, item []*Item, value [][]byte, policy pb.MemcacheSetRequest_SetPolicy) error {
if len(item) == 0 {
return nil
}
req := &pb.MemcacheSetRequest{
Item: make([]*pb.MemcacheSetRequest_Item, len(item)),
}
for i, t := range item {
p := &pb.MemcacheSetRequest_Item{
Key: []byte(t.Key),
}
if value == nil {
p.Value = t.Value
} else {
p.Value = value[i]
}
if t.Flags != 0 {
p.Flags = proto.Uint32(t.Flags)
}
if t.Expiration != 0 {
// In the .proto file, MemcacheSetRequest_Item uses a fixed32 (i.e. unsigned)
// for expiration time, while MemcacheGetRequest_Item uses int32 (i.e. signed).
// Throughout this .go file, we use int32.
// Also, in the proto, the expiration value is either a duration (in seconds)
// or an absolute Unix timestamp (in seconds), depending on whether the
// value is less than or greater than or equal to 30 years, respectively.
if t.Expiration < time.Second {
// Because an Expiration of 0 means no expiration, we take
// care here to translate an item with an expiration
// Duration between 0-1 seconds as immediately expiring
// (saying it expired a few seconds ago), rather than
// rounding it down to 0 and making it live forever.
p.ExpirationTime = proto.Uint32(uint32(time.Now().Unix()) - 5)
} else if t.Expiration >= thirtyYears {
p.ExpirationTime = proto.Uint32(uint32(time.Now().Unix()) + uint32(t.Expiration/time.Second))
} else {
p.ExpirationTime = proto.Uint32(uint32(t.Expiration / time.Second))
}
}
if t.casID != 0 {
p.CasId = proto.Uint64(t.casID)
p.ForCas = proto.Bool(true)
}
p.SetPolicy = policy.Enum()
req.Item[i] = p
}
res := &pb.MemcacheSetResponse{}
if err := internal.Call(c, "memcache", "Set", req, res); err != nil {
return err
}
if len(res.SetStatus) != len(item) {
return ErrServerError
}
me, any := make(appengine.MultiError, len(item)), false
for i, st := range res.SetStatus {
var err error
switch st {
case pb.MemcacheSetResponse_STORED:
// OK
case pb.MemcacheSetResponse_NOT_STORED:
err = ErrNotStored
case pb.MemcacheSetResponse_EXISTS:
err = ErrCASConflict
default:
err = ErrServerError
}
if err != nil {
me[i] = err
any = true
}
}
if any {
return me
}
return nil
}
// Set writes the given item, unconditionally.
func Set(c context.Context, item *Item) error {
return singleError(set(c, []*Item{item}, nil, pb.MemcacheSetRequest_SET))
}
// SetMulti is a batch version of Set.
// appengine.MultiError may be returned.
func SetMulti(c context.Context, item []*Item) error {
return set(c, item, nil, pb.MemcacheSetRequest_SET)
}
// Add writes the given item, if no value already exists for its key.
// ErrNotStored is returned if that condition is not met.
func Add(c context.Context, item *Item) error {
return singleError(set(c, []*Item{item}, nil, pb.MemcacheSetRequest_ADD))
}
// AddMulti is a batch version of Add.
// appengine.MultiError may be returned.
func AddMulti(c context.Context, item []*Item) error {
return set(c, item, nil, pb.MemcacheSetRequest_ADD)
}
// CompareAndSwap writes the given item that was previously returned by Get,
// if the value was neither modified or evicted between the Get and the
// CompareAndSwap calls. The item's Key should not change between calls but
// all other item fields may differ.
// ErrCASConflict is returned if the value was modified in between the calls.
// ErrNotStored is returned if the value was evicted in between the calls.
func CompareAndSwap(c context.Context, item *Item) error {
return singleError(set(c, []*Item{item}, nil, pb.MemcacheSetRequest_CAS))
}
// CompareAndSwapMulti is a batch version of CompareAndSwap.
// appengine.MultiError may be returned.
func CompareAndSwapMulti(c context.Context, item []*Item) error {
return set(c, item, nil, pb.MemcacheSetRequest_CAS)
}
// Codec represents a symmetric pair of functions that implement a codec.
// Items stored into or retrieved from memcache using a Codec have their
// values marshaled or unmarshaled.
//
// All the methods provided for Codec behave analogously to the package level
// function with same name.
type Codec struct {
Marshal func(interface{}) ([]byte, error)
Unmarshal func([]byte, interface{}) error
}
// Get gets the item for the given key and decodes the obtained value into v.
// ErrCacheMiss is returned for a memcache cache miss.
// The key must be at most 250 bytes in length.
func (cd Codec) Get(c context.Context, key string, v interface{}) (*Item, error) {
i, err := Get(c, key)
if err != nil {
return nil, err
}
if err := cd.Unmarshal(i.Value, v); err != nil {
return nil, err
}
return i, nil
}
func (cd Codec) set(c context.Context, items []*Item, policy pb.MemcacheSetRequest_SetPolicy) error {
var vs [][]byte
var me appengine.MultiError
for i, item := range items {
v, err := cd.Marshal(item.Object)
if err != nil {
if me == nil {
me = make(appengine.MultiError, len(items))
}
me[i] = err
continue
}
if me == nil {
vs = append(vs, v)
}
}
if me != nil {
return me
}
return set(c, items, vs, policy)
}
// Set writes the given item, unconditionally.
func (cd Codec) Set(c context.Context, item *Item) error {
return singleError(cd.set(c, []*Item{item}, pb.MemcacheSetRequest_SET))
}
// SetMulti is a batch version of Set.
// appengine.MultiError may be returned.
func (cd Codec) SetMulti(c context.Context, items []*Item) error {
return cd.set(c, items, pb.MemcacheSetRequest_SET)
}
// Add writes the given item, if no value already exists for its key.
// ErrNotStored is returned if that condition is not met.
func (cd Codec) Add(c context.Context, item *Item) error {
return singleError(cd.set(c, []*Item{item}, pb.MemcacheSetRequest_ADD))
}
// AddMulti is a batch version of Add.
// appengine.MultiError may be returned.
func (cd Codec) AddMulti(c context.Context, items []*Item) error {
return cd.set(c, items, pb.MemcacheSetRequest_ADD)
}
// CompareAndSwap writes the given item that was previously returned by Get,
// if the value was neither modified or evicted between the Get and the
// CompareAndSwap calls. The item's Key should not change between calls but
// all other item fields may differ.
// ErrCASConflict is returned if the value was modified in between the calls.
// ErrNotStored is returned if the value was evicted in between the calls.
func (cd Codec) CompareAndSwap(c context.Context, item *Item) error {
return singleError(cd.set(c, []*Item{item}, pb.MemcacheSetRequest_CAS))
}
// CompareAndSwapMulti is a batch version of CompareAndSwap.
// appengine.MultiError may be returned.
func (cd Codec) CompareAndSwapMulti(c context.Context, items []*Item) error {
return cd.set(c, items, pb.MemcacheSetRequest_CAS)
}
var (
// Gob is a Codec that uses the gob package.
Gob = Codec{gobMarshal, gobUnmarshal}
// JSON is a Codec that uses the json package.
JSON = Codec{json.Marshal, json.Unmarshal}
)
func gobMarshal(v interface{}) ([]byte, error) {
var buf bytes.Buffer
if err := gob.NewEncoder(&buf).Encode(v); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func gobUnmarshal(data []byte, v interface{}) error {
return gob.NewDecoder(bytes.NewBuffer(data)).Decode(v)
}
// Statistics represents a set of statistics about the memcache cache.
// This may include items that have expired but have not yet been removed from the cache.
type Statistics struct {
Hits uint64 // Counter of cache hits
Misses uint64 // Counter of cache misses
ByteHits uint64 // Counter of bytes transferred for gets
Items uint64 // Items currently in the cache
Bytes uint64 // Size of all items currently in the cache
Oldest int64 // Age of access of the oldest item, in seconds
}
// Stats retrieves the current memcache statistics.
func Stats(c context.Context) (*Statistics, error) {
req := &pb.MemcacheStatsRequest{}
res := &pb.MemcacheStatsResponse{}
if err := internal.Call(c, "memcache", "Stats", req, res); err != nil {
return nil, err
}
if res.Stats == nil {
return nil, ErrNoStats
}
return &Statistics{
Hits: *res.Stats.Hits,
Misses: *res.Stats.Misses,
ByteHits: *res.Stats.ByteHits,
Items: *res.Stats.Items,
Bytes: *res.Stats.Bytes,
Oldest: int64(*res.Stats.OldestItemAge),
}, nil
}
// Flush flushes all items from memcache.
func Flush(c context.Context) error {
req := &pb.MemcacheFlushRequest{}
res := &pb.MemcacheFlushResponse{}
return internal.Call(c, "memcache", "FlushAll", req, res)
}
func namespaceMod(m proto.Message, namespace string) {
switch m := m.(type) {
case *pb.MemcacheDeleteRequest:
if m.NameSpace == nil {
m.NameSpace = &namespace
}
case *pb.MemcacheGetRequest:
if m.NameSpace == nil {
m.NameSpace = &namespace
}
case *pb.MemcacheIncrementRequest:
if m.NameSpace == nil {
m.NameSpace = &namespace
}
case *pb.MemcacheSetRequest:
if m.NameSpace == nil {
m.NameSpace = &namespace
}
// MemcacheFlushRequest, MemcacheStatsRequest do not apply namespace.
}
}
func init() {
internal.RegisterErrorCodeMap("memcache", pb.MemcacheServiceError_ErrorCode_name)
internal.NamespaceMods["memcache"] = namespaceMod
}