blob: ad907caefc1ddeff062b7cf6c4e8db2f3848609f [file] [log] [blame]
// Copyright 2015 The LUCI Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
// Package gae provides a fakable wrapped interface for the appengine SDK's
// APIs. This means that it's possible to mock all of the supported appengine
// APIs for testing (or potentially implement a different backend for them).
// Features
// gae currently provides interfaces for:
// - Datastore
// - Memcache
// - TaskQueue
// - Info (e.g. Namespace, AppID, etc.)
// Additional features include:
// - true service interfaces (not package-level functions)
// - methods don't need explicit context passed to them, increasing readability.
// - service filters allow for composition of functionality. For example:
// - transparent memcaching of datastore access
// - transparent transaction buffering
// - statistics-gathering shims
// - deterministic and probabalistic API failure simulation
// - transparent retries
// - truly parallel in-memory testing implementation. No more need for
// subprocesses :).
// - Separate service and user-facing interfaces
// - Allows easier filter and service implementation while retaining the
// benefits of a user-friendly interface.
// Package Organization
// The gae library is organized into several subpackages:
// - service/* supported service definitions
// - impl/* implementations of the services
// - filter/* extra filter functionality for the services, agnostic to the
// underlying implementation.
// Service Definitions
// A service defintion lives under the `service` subfolder, and defines the
// user-facing interface for a service. Each service has a few common types and
// functions. Common types are:
// service.Interface - the main user-friendly service interface.
// service.RawInterface - the internal service interface used by service
// and filter implementations. Note that some services
// like Info don't distinguish between the service
// interface and the user interface. This interface is
// typically a bit lower level than Interface and
// lacks convenience methods.
// service.Testable - any additional methods that a 'testing'
// implementation should provide. This can be accessed
// via the Testable method on RawInterface. If the
// current implementation is not testable, it will
// return nil. This is only meant to be accessed when
// testing.
// service.RawFactory - a function returning a RawInterface
// service.RawFilter - a function returning a new RawInterface based on
// the previous filtered interface. Filters chain
// together to allow behavioral service features
// without needing to agument the underlying service
// implementations directly.
// And common functions are:
// service.GetRaw - Retrieve the current, filtered RawInterface
// implementation from the context. This is less
// frequently used, but can be useful if you want to
// avoid some of the overhead of the user-friendly
// Interface, which can do sometimes-unnecessary amounts
// of reflection or allocation. The RawInterface and
// Interface for a service are fully interchangable and
// usage of them can be freely mixed in an application.
// service.AddRawFilters - adds one or more RawFilters to the context.
// service.SetRawFactory - adds a RawFactory to the context
// service.SetRaw - adds an implementation of RawInterface to the context
// (shorthand for SetRawFactory, useful for testing)
// Implementations
// The impl subdirectory contains a couple different service implementations,
// depending on your needs.
// 'prod' is the production (e.g. real appengine-backed) implementation. It
// calls through to the original appengine SDK.
// 'memory' is a truly parallel in-memory testing implementation. It should
// be functionally the same as the production appengine services, implementing
// many of the real-world quirks of the actual services. It also implements
// the services' Testable interface, for those services which define those
// interfaces.
// 'dummy' provides a bunch of implementations of the various RawInterfaces.
// These implementations just panic with an appropriate message, depending on
// which API method was called. They're useful to embed in filter or service
// implementations as stubs while you're implementing the filter.
// Filters
// Each service also supports "filters". Filters are proxy objects which have
// the same interface as the service they're filtering, and pass data through to
// the previous filter in the stack. Conceptually, a filtered version of, for
// example, the Datastore, could look like:
// User code
// <count filter (counts how many times each API is called by the user)>
// <dscache filter (attempts to use memcache as a cache for datastore)>
// <count filter (counts how many times each API is actually hit)>
// memory datastore.RawInterface implementation
// Filters may or may not have state, it's up to the filter itself. In the case
// of the count filter, it returns its state from the Filter<Service> method,
// and the state can be observed to see how many times each API was invoked.
// Since filters stack, we can compare counts from rawCount versus userCount to
// see how many calls to the actual real datastore went through, vs. how many
// went to memcache, for example.
// Note that Filters apply only to the service.RawInterface. All implementations
// of service.Interface boil down to calls to service.RawInterface methods, but
// it's possible that bad calls to the service.Interface methods could return
// an error before ever reaching the filters or service implementation.
package gae