go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/gae/doc.go (about)

     1  // Copyright 2015 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package gae provides a fakable wrapped interface for the appengine SDK's
    16  // APIs. This means that it's possible to mock all of the supported appengine
    17  // APIs for testing (or potentially implement a different backend for them).
    18  //
    19  // # Features
    20  //
    21  // gae currently provides interfaces for:
    22  //   - Datastore
    23  //   - Memcache
    24  //   - TaskQueue
    25  //   - Info (e.g. Namespace, AppID, etc.)
    26  //
    27  // Additional features include:
    28  //   - true service interfaces (not package-level functions)
    29  //   - methods don't need explicit context passed to them, increasing readability.
    30  //   - service filters allow for composition of functionality. For example:
    31  //   - transparent memcaching of datastore access
    32  //   - transparent transaction buffering
    33  //   - statistics-gathering shims
    34  //   - deterministic and probabalistic API failure simulation
    35  //   - transparent retries
    36  //   - truly parallel in-memory testing implementation. No more need for
    37  //     dev_appserver.py subprocesses :).
    38  //   - Separate service and user-facing interfaces
    39  //   - Allows easier filter and service implementation while retaining the
    40  //     benefits of a user-friendly interface.
    41  //
    42  // # Package Organization
    43  //
    44  // The gae library is organized into several subpackages:
    45  //   - service/*   supported service definitions
    46  //   - impl/*      implementations of the services
    47  //   - filter/*    extra filter functionality for the services, agnostic to the
    48  //     underlying implementation.
    49  //
    50  // # Service Definitions
    51  //
    52  // A service defintion lives under the `service` subfolder, and defines the
    53  // user-facing interface for a service. Each service has a few common types and
    54  // functions. Common types are:
    55  //
    56  //	service.Interface    - the main user-friendly service interface.
    57  //
    58  //	service.RawInterface - the internal service interface used by service
    59  //	                       and filter implementations. Note that some services
    60  //	                       like Info don't distinguish between the service
    61  //	                       interface and the user interface. This interface is
    62  //	                       typically a bit lower level than Interface and
    63  //	                       lacks convenience methods.
    64  //
    65  //	service.Testable     - any additional methods that a 'testing'
    66  //	                       implementation should provide. This can be accessed
    67  //	                       via the Testable method on RawInterface. If the
    68  //	                       current implementation is not testable, it will
    69  //	                       return nil. This is only meant to be accessed when
    70  //	                       testing.
    71  //
    72  //	service.RawFactory   - a function returning a RawInterface
    73  //
    74  //	service.RawFilter    - a function returning a new RawInterface based on
    75  //	                       the previous filtered interface. Filters chain
    76  //	                       together to allow behavioral service features
    77  //	                       without needing to agument the underlying service
    78  //	                       implementations directly.
    79  //
    80  // And common functions are:
    81  //
    82  //	service.GetRaw        - Retrieve the current, filtered RawInterface
    83  //	                        implementation from the context. This is less
    84  //	                        frequently used, but can be useful if you want to
    85  //	                        avoid some of the overhead of the user-friendly
    86  //	                        Interface, which can do sometimes-unnecessary amounts
    87  //	                        of reflection or allocation. The RawInterface and
    88  //	                        Interface for a service are fully interchangable and
    89  //	                        usage of them can be freely mixed in an application.
    90  //
    91  //	service.AddRawFilters - adds one or more RawFilters to the context.
    92  //
    93  //	service.SetRawFactory - adds a RawFactory to the context
    94  //
    95  //	service.SetRaw        - adds an implementation of RawInterface to the context
    96  //	                        (shorthand for SetRawFactory, useful for testing)
    97  //
    98  // # Implementations
    99  //
   100  // The impl subdirectory contains a couple different service implementations,
   101  // depending on your needs.
   102  //
   103  // 'prod' is the production (e.g. real appengine-backed) implementation. It
   104  // calls through to the original appengine SDK.
   105  //
   106  // 'memory' is a truly parallel in-memory testing implementation. It should
   107  // be functionally the same as the production appengine services, implementing
   108  // many of the real-world quirks of the actual services. It also implements
   109  // the services' Testable interface, for those services which define those
   110  // interfaces.
   111  //
   112  // 'dummy' provides a bunch of implementations of the various RawInterfaces.
   113  // These implementations just panic with an appropriate message, depending on
   114  // which API method was called. They're useful to embed in filter or service
   115  // implementations as stubs while you're implementing the filter.
   116  //
   117  // # Filters
   118  //
   119  // Each service also supports "filters". Filters are proxy objects which have
   120  // the same interface as the service they're filtering, and pass data through to
   121  // the previous filter in the stack. Conceptually, a filtered version of, for
   122  // example, the Datastore, could look like:
   123  //
   124  //	User code
   125  //	<count filter (counts how many times each API is called by the user)>
   126  //	<dscache filter (attempts to use memcache as a cache for datastore)>
   127  //	<count filter (counts how many times each API is actually hit)>
   128  //	memory datastore.RawInterface implementation
   129  //
   130  // Filters may or may not have state, it's up to the filter itself. In the case
   131  // of the count filter, it returns its state from the Filter<Service> method,
   132  // and the state can be observed to see how many times each API was invoked.
   133  // Since filters stack, we can compare counts from rawCount versus userCount to
   134  // see how many calls to the actual real datastore went through, vs. how many
   135  // went to memcache, for example.
   136  //
   137  // Note that Filters apply only to the service.RawInterface. All implementations
   138  // of service.Interface boil down to calls to service.RawInterface methods, but
   139  // it's possible that bad calls to the service.Interface methods could return
   140  // an error before ever reaching the filters or service implementation.
   141  package gae