-
Notifications
You must be signed in to change notification settings - Fork 0
/
ds.go
208 lines (188 loc) · 6.26 KB
/
ds.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
// Copyright 2012 GAEGo Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Package ds provides cached persistence for the Google App Engine datastore.
*/
package ds
// TODO(kylefinley) Ideally this package would allow for a more modular approach.
// The API would look more like this:
//
// Register("Entity", &memcache.Store{}, &datastore.Store{})
//
// Stores would be listed in the order in which they should be check. This approach
// would allow for custom stores to be easily created as well. Creating A MongoDB
// Store would be as simple as creating a Store that implements Storer, and passing
// a pointer to the Register() method.
//
// The end goal is to create a consistent interface for all types of data persistence.
// TODO(kylefinley) add support for gob encoding of invalid datastore types.
// E.g.
// type T struct {
// A map[string]interface{} `ds:"gob"`
// }
// Would encode T.A to gob
// Or maybe this should be handled by ds/datastore.
import (
"appengine"
"appengine/datastore"
"fmt"
dsdatastore "github.com/gaego/ds/appengine/datastore"
dsmemcache "github.com/gaego/ds/appengine/memcache"
dsmemory "github.com/gaego/ds/memory"
"time"
)
func init() {
// Set the default store to memcache > datastore.
Register("default", true, true, false)
}
var (
kinds = make(map[string]*StoreConfig)
Kinds = kinds
dsds = dsdatastore.New()
memcache = dsmemcache.New()
memory = dsmemory.New()
)
// StoreConfig is used to establish the stores that should be used for an Entity.
// A true value indicated that that store should be used. A false that that store
// should not be used.
type StoreConfig struct {
Datastore, Memcache, Memory bool
}
// Register takes a string representing the entity kind, followed by bools for
// datastore, memcache and memory with a value of true indicating that the entity
// should be stored in that store.
//
// Register("Product", true, true, false)
//
// Would set the StoreConfig for products. Based on the config "Product" entities
// will be stored to the datastore, and memcache, but not memory.
func Register(kind string, useDatastore, useMemcache, useMemory bool) {
kinds[kind] = &StoreConfig{useDatastore, useMemcache, useMemory}
return
}
// getConfig returns a StoreConfig based on entity kind. If the entity kind
// has not been saved the default StoreConfig is returned.
func getConfig(knd string) *StoreConfig {
s, ok := kinds[knd]
if !ok {
s = kinds["default"]
}
return s
}
// PutMulti given a []*datastore.Key and a list of struct pointers adds
// multiple entities to the store.
func PutMulti(c appengine.Context, key []*datastore.Key, src interface{}) ([]*datastore.Key, error) {
// TODO(kylefinley) After the datastore put this method should also put
// to the various caches that the entity belongs.
key, err := dsds.PutMulti(c, key, src)
// Clear caches
_ = memcache.DeleteMulti(c, key)
_ = memory.DeleteMulti(c, key)
return key, err
}
// Put given a *datastore.Key and a struct pointer adds a single entity
// to the store.
func Put(c appengine.Context, key *datastore.Key, src interface{}) (*datastore.Key, error) {
sc := getConfig(key.Kind())
key, err := dsds.Put(c, key, src)
if err != nil {
return key, err
}
if sc.Memcache {
key, err = memcache.Put(c, key, src)
}
if sc.Memory {
key, err = memory.Put(c, key, src)
}
return key, err
}
// GetMulti given a []*datastore.Key returns multiple entities from the store
func GetMulti(c appengine.Context, key []*datastore.Key, dst interface{}) (err error) {
// TODO(kylefinley) It would save time and quota if this method only pulled
// from the datastore values that where not cached.
// The challenge arises when loading keys from different entity groups.
// Each entity group may have different storage configurations, which
// complicates things.
// For the time being pull entity directly from the datastore.
return dsds.GetMulti(c, key, dst)
}
// Get given a *datastore.Key returns a single entity from the store
func Get(c appengine.Context, key *datastore.Key, dst interface{}) (err error) {
sc := getConfig(key.Kind())
needMemory := true
needMemcache := true
if sc.Memory {
err = memory.Get(c, key, dst)
if err == nil {
needMemory = false
goto Complete
}
}
if sc.Memcache {
err = memcache.Get(c, key, dst)
if err == nil {
needMemcache = false
goto Complete
}
}
if sc.Datastore {
err = dsds.Get(c, key, dst)
if err == nil {
goto Complete
}
}
return
Complete:
// Save the entity to any caches that it should be saved to.
if sc.Memcache && needMemcache {
_, _ = memcache.Put(c, key, dst)
}
if sc.Memory && needMemory {
_, _ = memory.Put(c, key, dst)
}
return
}
// DeleteMulti given a []*datastore.Key deletes multiple entities from the store
func DeleteMulti(c appengine.Context, key []*datastore.Key) (err error) {
if len(key) == 0 {
return nil
}
err = dsds.DeleteMulti(c, key)
_ = memcache.DeleteMulti(c, key)
_ = memory.DeleteMulti(c, key)
return
}
// Delete given a *datastore.Key deletes a single entity from the store
func Delete(c appengine.Context, key *datastore.Key) (err error) {
err = dsds.Delete(c, key)
_ = memcache.Delete(c, key)
_ = memory.Delete(c, key)
return
}
func AllocateIDs(c appengine.Context, kind string, parent *datastore.Key, n int) (low, high int64, err error) {
// TODO: added for testing. Allocating IDs for mememache and memory
// should not be used in production.
sc := getConfig(kind)
if sc.Datastore {
return datastore.AllocateIDs(c, kind, parent, n)
}
t := time.Now()
//var l int64
l := t.UnixNano()
return l, l + int64(n), nil
}
// AllocateID ruturns a single Id and returns it as a string.
func AllocateID(c appengine.Context, kind string) (string, error) {
id, _, err := AllocateIDs(c, kind, nil, 1)
return fmt.Sprint(id), err
}
// Storer is not implemented yet.
// type Storer interface {
// Get(appengine.Context, *datastore.Key, interface{}) error
// GetMulti(appengine.Context, []*datastore.Key, interface{}) error
// Put(appengine.Context, *datastore.Key, interface{}) (*datastore.Key, error)
// PutMulti(appengine.Context, []*datastore.Key, interface{}) ([]*datastore.Key, error)
// Delete(appengine.Context, *datastore.Key) error
// DeleteMulti(appengine.Context, []*datastore.Key) error
// }