Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(278)

Side by Side Diff: doc.go

Issue 1259593005: Add 'user friendly' datastore API. (Closed) Base URL: https://github.com/luci/gae.git@master
Patch Set: 100% coverage of new code Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | filter/count/count_test.go » ('j') | service/datastore/checkfilter_test.go » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // Package gae provides a fakable wrapped interface for the appengine SDK's 5 // Package gae provides a fakable wrapped interface for the appengine SDK's
6 // APIs. This means that it's possible to mock all of the supported appengine 6 // APIs. This means that it's possible to mock all of the supported appengine
7 // APIs for testing (or potentially implement a different backend for them). 7 // APIs for testing (or potentially implement a different backend for them).
8 // 8 //
9 // Features 9 // Features
10 // 10 //
11 // gae currently provides interfaces for: 11 // gae currently provides interfaces for:
12 // - RawDatastore (a less reflection-magic version of Datastore) 12 // - Datastore
13 // - Memcache 13 // - Memcache
14 // - TaskQueue 14 // - TaskQueue
15 // - GlobalInfo (e.g. Namespace, AppID, etc.) 15 // - Info (e.g. Namespace, AppID, etc.)
16 //
17 // In addition, it provides a 'Datastore' service which adds all the reflection
18 // magic of the original SDK's datastore package on top of RawDatastore.
19 // 16 //
20 // Additional features include: 17 // Additional features include:
21 // - true service interfaces (not package-level functions) 18 // - true service interfaces (not package-level functions)
22 // - methods don't need explicit context passed to them, increasing readabilit y. 19 // - methods don't need explicit context passed to them, increasing readabilit y.
23 // - service filters allow for composition of functionality. For example: 20 // - service filters allow for composition of functionality. For example:
24 // - transparent memcaching of datastore access 21 // - transparent memcaching of datastore access
25 // - transparent transaction buffering 22 // - transparent transaction buffering
26 // - statistics-gathering shims 23 // - statistics-gathering shims
27 // - deterministic and probabalistic API failure simulation 24 // - deterministic and probabalistic API failure simulation
28 // - transparent retries 25 // - transparent retries
29 // - truly parallel in-memory testing implementation. No more need for 26 // - truly parallel in-memory testing implementation. No more need for
30 // dev_appserver.py subprocesses :). 27 // dev_appserver.py subprocesses :).
28 // - Separate service and user-facing interfaces
29 // - Allows easier filter and service implementation while retaining the
30 // benefits of a user-friendly interface.
31 // 31 //
32 // Package Organization 32 // Package Organization
33 // 33 //
34 // The gae library is organized into several subpackages: 34 // The gae library is organized into several subpackages:
35 // - service/* supported service definitions 35 // - service/* supported service definitions
36 // - impl/* implementations of the services 36 // - impl/* implementations of the services
37 // - filter/* extra filter functionality for the services, agnostic to the 37 // - filter/* extra filter functionality for the services, agnostic to the
38 // underlying implementation. 38 // underlying implementation.
39 // 39 //
40 // TLDR 40 // TLDR
41 // 41 //
42 // In production, do: 42 // In production, do:
43 // 43 //
44 // import ( 44 // import (
45 // "fmt" 45 // "fmt"
46 // "net/http" 46 // "net/http"
47 // 47 //
48 // "github.com/luci/gae/impl/prod" 48 // "github.com/luci/gae/impl/prod"
49 // "github.com/luci/gae/service/rawdatastore" 49 // "github.com/luci/gae/service/datastore"
50 // "golang.org/x/net/context" 50 // "golang.org/x/net/context"
51 // ) 51 // )
52 // 52 //
53 // func handler(w http.ResponseWriter, r *http.Request) { 53 // func handler(w http.ResponseWriter, r *http.Request) {
54 // c := prod.UseRequest(r) 54 // c := prod.UseRequest(r)
55 // // add production filters, etc. here 55 // // add production filters, etc. here
56 // innerHandler(c, w) 56 // innerHandler(c, w)
57 // } 57 // }
58 // 58 //
59 // type CoolStruct struct {
60 // ID `gae:"$id"`
61 //
62 // Value string
63 // }
64 //
59 // func innerHandler(c context.Context, w http.ResponseWriter) { 65 // func innerHandler(c context.Context, w http.ResponseWriter) {
60 // rds := rawdatastore.Get(c) 66 // rds := datastore.Get(c)
61 // data := rawdatastore.PropertyMap{ 67 // obj := &CoolStruct{Value: "hello"}
62 // "Value": {rawdatastore.MkProperty("hello")}, 68 // if err := rds.Put(obj); err != nil {
63 // }
64 // newKey, err := rds.Put(rds.NewKey("Kind", "", 0, nil), data)
65 // if err != nil {
66 // http.Error(w, err.String(), http.StatusInternalServerError) 69 // http.Error(w, err.String(), http.StatusInternalServerError)
67 // } 70 // }
68 // fmt.Fprintf(w, "I wrote: %s", newKey) 71 // fmt.Fprintf(w, "I wrote: %s", ds.KeyForObj(obj))
69 // } 72 // }
70 // 73 //
71 // And in your test do: 74 // And in your test do:
72 // 75 //
73 // import ( 76 // import (
74 // "testing" 77 // "testing"
75 // "fmt" 78 // "fmt"
76 // "net/http" 79 // "net/http"
77 // 80 //
78 // "github.com/luci/gae/impl/memory" 81 // "github.com/luci/gae/impl/memory"
79 // "github.com/luci/gae/service/rawdatastore" 82 // "github.com/luci/gae/service/datastore"
80 // "golang.org/x/net/context" 83 // "golang.org/x/net/context"
81 // ) 84 // )
82 // 85 //
83 // func TestHandler(t *testing.T) { 86 // func TestHandler(t *testing.T) {
84 // t.Parallel() 87 // t.Parallel()
85 // c := memory.Use(context.Background()) 88 // c := memory.Use(context.Background())
86 // // use rawdatastore here to monkey with the database, install 89 // // use datastore here to monkey with the database, install testing
87 // // testing filters like featureBreaker to test error conditions in 90 // // filters like featureBreaker to test error conditions in innerHandler,
88 // // innerHandler, etc. 91 // // etc.
89 // innerHandler(c, ...) 92 // innerHandler(c, ...)
90 // } 93 // }
91 // 94 //
92 // Service Definitions 95 // Service Definitions
93 // 96 //
94 // A service defintion lives under the `service` subfolder, and defines the 97 // A service defintion lives under the `service` subfolder, and defines the
95 // user-facing interface for a service. Each service has a few common types and 98 // user-facing interface for a service. Each service has a few common types and
96 // functions. Common types are: 99 // functions. Common types are:
97 // 100 //
98 // service.Interface - the main service interface 101 // service.Interface - the main user-friendly service interface.
99 // service.Testable - any additional methods that a 'testing' implementation 102 //
100 // should provide. It's expected that tests will cast 103 // service.RawInterface - the internal service interface used by service
101 // the Interface from Get() to Testable in order to 104 // and filter implementations. Note that some services
102 // access these methods. 105 // like Info don't distinguish between the service
103 // service.Factory - a function returning an Interface 106 // interface and the user interface.
104 // service.Filter - a function returning a new Interface based on the 107 //
105 // previous filtered interface. 108 // service.Testable - any additional methods that a 'testing'
109 // implementation should provide. It's expected that
110 // tests will cast the RawInterface from GetRaw() to
111 // Testable in order to access these methods.
112 //
113 // service.RawFactory - a function returning a RawInterface
114 //
115 // service.RawFilter - a function returning a new RawInterface based on
116 // the previous filtered interface. Filters chain
117 // together to allow behavioral service features
118 // without needing to agument the underlying service
119 // implementations directly.
106 // 120 //
107 // And common functions are: 121 // And common functions are:
108 // service.Get - Retrieve the current, filtered Interface 122 // service.Get - Retrieve the current, filtered Interface
109 // implementation from the context. This is the most 123 // implementation from the context. This is the most
110 // frequently used service function by far. 124 // frequently used service function by far.
111 // service.AddFilters - adds one or more Filters to the context. 125 //
112 // service.SetFactory - adds a Factory to the context 126 // service.GetRaw - Retrieve the current, filtered RawInterface
113 // service.Set - adds an implementation of Interface to the context 127 // implementation from the context. This is less
114 // (shorthand for SetFactory, useful for testing) 128 // frequently used, but can be useful if you want to
129 // avoid some of the overhead of the user-friendly
130 // Interface, which can do sometimes-unnecessary amou nts
131 // of reflection or allocation. The RawInterface and
132 // Interface for a service are fully interchangable a nd
133 // usage of them can be freely mixed in an applicatio n.
134 //
135 // service.AddRawFilters - adds one or more Filters to the context.
136 //
137 // service.SetRawFactory - adds a Factory to the context
138 //
139 // service.SetRaw - adds an implementation of RawInterface to the cont ext
140 // (shorthand for SetRawFactory, useful for testing)
115 // 141 //
116 // Implementations 142 // Implementations
117 // 143 //
118 // The impl subdirectory contains a couple different service implementations, 144 // The impl subdirectory contains a couple different service implementations,
119 // depending on your needs. 145 // depending on your needs.
120 // 146 //
121 // 'prod' is the production (e.g. real appengine-backed) implementation. It 147 // 'prod' is the production (e.g. real appengine-backed) implementation. It
122 // calls through to the original appengine SDK. 148 // calls through to the original appengine SDK.
123 // 149 //
124 // 'memory' is a truly parallel in-memory testing implementation. It should 150 // 'memory' is a truly parallel in-memory testing implementation. It should
125 // be functionally the same as the production appengine services, implementing 151 // be functionally the same as the production appengine services, implementing
126 // many of the real-world quirks of the actual services. It also implements 152 // many of the real-world quirks of the actual services. It also implements
127 // the services' Testable interface, for those services which define those 153 // the services' Testable interface, for those services which define those
128 // interfaces. 154 // interfaces.
129 // 155 //
156 // 'dummy' provides a bunch of implementations of the various RawInterfaces.
157 // These implementations just panic with an appropriate message, depending on
158 // which API method was called. They're useful to embed in filter or service
159 // implementations as stubs while you're implementing the filter.
160 //
130 // Usage 161 // Usage
131 // 162 //
132 // You will typically access one of the service interfaces in your code like: 163 // You will typically access one of the service interfaces in your code like:
133 // // This is the 'production' code 164 // // This is the 'production' code
134 // func HTTPHandler(r *http.Request) { 165 // func HTTPHandler(r *http.Request) {
135 // c := prod.Use(appengine.NewContext(r)) 166 // c := prod.Use(appengine.NewContext(r))
136 // CoolFunc(c) 167 // CoolFunc(c)
137 // } 168 // }
138 // 169 //
139 // // This is the 'testing' code 170 // // This is the 'testing' code
140 // func TestCoolFunc(t *testing.T) { 171 // func TestCoolFunc(t *testing.T) {
141 // c := memory.Use(context.Background()) 172 // c := memory.Use(context.Background())
142 // CoolFunc(c) 173 // CoolFunc(c)
143 // } 174 // }
144 // 175 //
145 // func CoolFunc(c context.Context, ...) { 176 // func CoolFunc(c context.Context, ...) {
146 // rds := gae.GetRDS(c) // returns a RawDatastore object 177 // ds := datastore.Get(c) // returns a datastore.Interface object
147 // mc := gae.GetMC(c) // returns a Memcache object 178 // mc := memcache.Get(c) // returns a memcache.Interface object
148 // // use them here 179 // // use them here
149 // 180 //
150 // // don't pass rds/mc/etc. directly, pass the context instead. 181 // // don't pass ds/mc/etc. directly, pass the context instead.
151 // SomeOtherFunction(c, ...) 182 // SomeOtherFunction(c, ...)
152 // 183 //
153 // // because you might need to: 184 // // because you might need to:
154 // rds.RunInTransaction(func (c context.Context) error { 185 // ds.RunInTransaction(func (c context.Context) error {
155 // SomeOtherFunction(c, ...) // c contains transaction versions of everyt hing 186 // SomeOtherFunction(c, ...) // c contains transactional versions of ever ything
156 // }, nil) 187 // }, nil)
157 // } 188 // }
158 // 189 //
159 // RawDatastore struct serialization is provided by the `rawdatastore`
160 // subpackage. All supported struct types and interfaces are provided in this
161 // package, however. You can operate without any struct
162 // serizialization/reflection by exclusively using PropertyMap. A goon-style
163 // Datastore interface is also provided in the `datastore` service package.
164 //
165 // Filters 190 // Filters
166 // 191 //
167 // Each service also supports "filters". Filters are proxy objects which have 192 // Each service also supports "filters". Filters are proxy objects which have
168 // the same interface as the service they're filtering, and pass data through to 193 // the same interface as the service they're filtering, and pass data through to
169 // the previous filter in the stack. Conceptually, a filtered version of, for 194 // the previous filter in the stack. Conceptually, a filtered version of, for
170 // example, the RawDatastore, could look like: 195 // example, the Datastore, could look like:
171 // User code 196 // User code
172 // <count filter (counts how many times each API is called by the user)> 197 // <count filter (counts how many times each API is called by the user)>
173 // <mcache filter (attempts to use memcache as a cache for rawdatastore)> 198 // <dscache filter (attempts to use memcache as a cache for datastore)>
174 // <count filter (counts how many times each API is actually hit)> 199 // <count filter (counts how many times each API is actually hit)>
175 // memory RawDatastore implementation 200 // memory datastore.RawInterface implementation
176 // 201 //
177 // So rawdatastore.Get would return the full stack. In code, this would look 202 // So datastore.Get would return the full stack. In code, this would look
178 // like: 203 // like:
179 // func HTTPHandler(r *http.Request) { 204 // func HTTPHandler(r *http.Request) {
180 // c := prod.Use(appengine.NewContext(r)) // production datastore 205 // c := prod.UseRequest(r)» » » » » » » // production datastore
181 // c, rawCount := count.FilterRDS() // add count filter 206 // c, rawCount := count.FilterRDS(c) // add count filter
182 // c = mcache.FilterRDS(c) // add mcache filter 207 // c = dscache.FilterRDS(c) // add dscache filter
183 // c, userCount := count.FilterRDS() // add another count filter 208 // c, userCount := count.FilterRDS(c) // add another count filter
184 // } 209 // }
185 // 210 //
186 // Filters may or may not have state, it's up to the filter itself. In the case 211 // Filters may or may not have state, it's up to the filter itself. In the case
187 // of the count filter, it returns its state from the Filter<Service> method, 212 // of the count filter, it returns its state from the Filter<Service> method,
188 // and the state can be observed to see how many times each API was invoked. 213 // and the state can be observed to see how many times each API was invoked.
189 // Since filters stack, we can compare counts from rawCount versus userCount to 214 // Since filters stack, we can compare counts from rawCount versus userCount to
190 // see how many calls to the actual real datastore went through, vs. how many 215 // see how many calls to the actual real datastore went through, vs. how many
191 // went to memcache, for example. 216 // went to memcache, for example.
217 //
218 // Note that Filters apply only to the service.RawInterface. All implementations
219 // of service.Interface boil down to calls to service.RawInterface methods, but
220 // it's possible that bad calls to the service.Interface methods could return
221 // an error before ever reaching the filters or service implementation.
192 package gae 222 package gae
OLDNEW
« no previous file with comments | « no previous file | filter/count/count_test.go » ('j') | service/datastore/checkfilter_test.go » ('J')

Powered by Google App Engine
This is Rietveld 408576698