syncstorage/storage/kvstore/__init__.py
author Ryan Kelly <ryan@rfk.id.au>
Tue, 13 Sep 2011 19:47:27 +1000
changeset 1 db5a8ac3fd16d7477fabcd578230663fd0b71478
parent 0 90f1d3fa8f61c1bba10a55567bf15ad48a9ff500
permissions -rw-r--r--
Add more docs to KVSStorage implementation

# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Sync Server
#
# The Initial Developer of the Original Code is the Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#   Ryan Kelly (ryan@rfk.id.au)
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
"""

Abstract intrface to key-value storage.

This is all experimental and still in the design phase.
See here for details:

    https://wiki.mozilla.org/Services/KeyValueStorage

"""


import abc
from services.pluginreg import PluginRegistry


class VersionMismatchError(Exception):
    """Exception raised when edits are based on an old version.

    This exception will be raised by a KVStore if you request an option
    with the 'ifmatch' argument, and the version in the store does not
    match the specified version.

    Think of it like a HTTP "412 Precondition Failed" returned in response
    to a mismatched "If-Match" header.
    """
    pass


class KVStore(PluginRegistry):
    """Abstract Base Class for key-value storage.

    This class defines the abstract interface for applications to access
    the "key-value storage" service.

    A KVStore associates each user with a list of named "buckets" which can
    be used for simplistic key-value storage.
    """
    plugin_type = "kvstore"

    @abc.abstractmethod
    def get_name(self):
        """Returns the name of the plugin.

        Must be a class method.

        Args:
            None

        Returns:
            The plugin name
        """


    @abc.abstractmethod
    def get_bucket(self, user_id, bucket_name):
        """Access an individual KVStore bucket for the given user.

        Args:
            user_id:  string identifying the user owning the bucket.
            bucket_name:  string naming the bucket to access.

        Returns:
            A KVBucket object providing access to the requested bucket.
        """

    @abc.abstractmethod
    def create_bucket(self, user_id, bucket_name):
        """Create a new KVStore bucket for the given user.

        Args:
            user_id:  string identifying the user who will own
            bucket_name:  string naming the bucket to create.

        Returns:
            A KVBucket object providing access to the newly-created bucket.
        """

    @abc.abstractmethod
    def delete_bucket(self, user_id, bucket_name):
        """Delete a KVStore bucket belonging to the given user.

        Args:
            user_id:  string identifying the user owning the bucket.
            bucket_name:  string naming the bucket to delete.

        Returns:
            None
        """

    @abc.abstractmethod
    def bucket_exists(self, user_id, bucket_name):
        """Check whether a KVStore bucket exists for the given user.

        Args:
            user_id:  string identifying the user owning the bucket.
            bucket_name:  string naming the bucket to check.

        Returns:
            True or False
        """

    @abc.abstractmethod
    def list_buckets(self, user_id):
        """List all KVStore buckets belonging to the given user.

        Args:
            user_id:  string identifying the user.

        Returns:
            List of Strings naming buckets for the given user.
        """


class KVSBucket(object):
    """Individual bucket in a KVStore."""
    __metaclass__ = abc.ABCMeta

    def __init__(self, store, name):
        self.store = store
        self.name = name

    @abc.abstractmethod
    def get(self, key):
        """Retrieve the value for a given key.

        Args:
            key:  string naming the key to load.

        Returns:
            String value corresponding to the given key.

        Raises:
            KeyError:  the specified key does not exist in the store
        """

    @abc.abstractmethod
    def getitem(self, key):
        """Retrieve the KVItem object for a given key.

        Args:
            key:  string naming the key to load.

        Returns:
            KVItem giving the value and version for the given key.

        Raises:
            KeyError:  the specified key does not exist in the store
        """

    @abc.abstractmethod
    def set(self, key, value, ifmatch=None, indexes=None):
        """Store a new value under the given key.

        Args:
            key:  string naming the key under which to store the value.
            value:  string value to store under the given key.
            ifmatch:  optional string identifying the previous version
                      of the data, to prevent lost updates.
            indexes:  dict mapping string index names to string index values,
                      to update secondary indexes pointing at this key.

        Returns:
            KVItem giving the updated value and version for the given key.

        Raises:
            VersionMismatchError:  the version specified in 'ifmatch' does
                                   not match the current version in the store.
        """

    @abc.abstractmethod
    def delete(self, key, ifmatch=None):
        """Delete the specified key from the store.

        Args:
            key:  string naming the key to be deleted.
            ifmatch:  optional string identifying the previous version
                      of the data, to prevent lost updates.

        Returns:
            None

        Raises:
            KeyError:  the specified key does not exist in the store
            VersionMismatchError:  the version specified in 'ifmatch' does
                                   not match the current version in the store.
        """

    @abc.abstractmethod
    def scan(self, index_name, min=None, max=None, limit=None, reverse=False):
        """Scan through keys associated with a secondary index.

        Args:
            index_name:  string naming the index to scan through.
            min:  string giving lower bound on index values to scan.
            max:  string giving upper bound on index values to scan.
            limit:  integer giving maximum number of results to yield.
            reverse:  bool indicating whether to scan in descending order.

        Returns:
            Iterator yielding (index_value, key) pairs from the index.
        """


class KVSItem(object):
    """Individual item in a KVStore.

    This class holds all the information about a single item stored in
    a KVStore: the owning bucket, the key, the value, and the version string.
    """
    __metaclass__ = abc.ABCMeta

    def __init__(self, bucket, key, value, version):
        self.bucket = bucket
        self.key = key
        self.value = value
        self.version = version