keystore

package module
v0.0.0-...-f7807ac Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 25, 2018 License: MIT Imports: 16 Imported by: 0

README

Простейшее key-value хранилище для Golang

GoDoc Build Status Coverage Status

Все данные хранятся в одном файле и не используют отдельного индекса. При первом открытии хранилища происходит построение индекса с содержимым файла и проверка целостности хранилища. Эта операция может занимать некоторое время при открытии действительно больших файлов, поэтому данная библиотека расчитана в первую очередь на небольшие хранилища.

По умолчанию все записи в хранилище заканчиваются вызовом метода os.File.Sync, что позволяет быть до некоторой степени уверенными, что данные при сбое не потеряются. Но, к сожалению, это одновременно сильно замедляет любую операцию записи. Если вы хотите самостоятельно управлять операциями сброса кешей файловой системы, то можно вызвать метод db.SetSync(false) и затем вызывать метод db.Sync вручную из кода.

При удалении или перезаписи значений, свободные участки помечаются специальным образом и в дальнейшем используются повторно, когда в них может уместиться новая запись. Таким образом файл не очень сильно разрастается при большом количестве удалений/вставок новый записей, при условии, что новые значения не превышают по объему удаленные.

При работе с хранилищем поддерживаются групповые операции: db.Gets, db.Puts, db.Deletes. Так же добавлены методы для работы с данными в формате JSON и автоматического преобразования объектов в/из него.

Работа с ключами хранилища и выборки данных поддерживается единственным методом db.Keys, который позволяет достаточно гибко выбирать только те ключи, которые соответствуют заданным критериям. Список ключей всегда возвращается в упорядоченном виде и может быть использован в дальнейшем для выполнения групповых операций.

Для облегчения работы с данной библиотекой все методы хранилища продублированы в виде глобальных функций, где первым параметром указывается имя файла с хранилищем. Открытые таким образом хранилища кешируются в глобальном списке и могут быть закрыты все сразу вызовом CloseAll.

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/mdigger/keystore"
)

func main() {
	// автоматически закрыть по окончании все открытые хранилища
	defer keystore.CloseAll()
	var dbname = "test.db" // имя файла с хранилищем данных
	// сохраняем данные в хранилище в формате JSON
	// если такого файла с хранилищем не существует, то он будет создан
	// автоматически
	err := keystore.PutsJSON(dbname, map[string]interface{}{
		"t1": "text message",
		"t2": 24,
		"t3": time.Date(1971, time.December, 24, 23, 0, 0, 0, time.UTC),
		"t4": &struct {
			Text string `json:"text"`
		}{
			Text: "test",
		},
	})
	if err != nil {
		log.Fatal("PutsJSON error:", err)
	}
	// выбираем все ключи, сохраненные в хранилище, которые начинаются на `t`
	keys, err := keystore.Keys(dbname, "t", "", 0, 0, true)
	if err != nil {
		log.Fatal("Keys error:", err)
	}
	// получаем список значений из выборки по ключам
	result, err := keystore.GetsJSON(dbname, keys...)
	if err != nil {
		log.Fatal("GetsJSON error:", err)
	}
	// выводим выбранные значения в консоль в виде JSON
	enc := json.NewEncoder(os.Stdout)
	enc.SetIndent("", "  ")
	err = enc.Encode(result)
	if err != nil {
		log.Fatal("JSON encode error:", err)
	}
}
Примеры выборок ключей
  • выбираем все ключи, которые начинаются на test

      db.Keys("test", "", 0, 0, true)
    
  • выбираем все ключи, которые начинаются на test, но после ключа test2

      db.Keys("test", "test2", 0, 0, true)
    
  • сортируем вывод в обратном порядке

      db.Keys("", "", 0, 0, false)
    
  • выбираем не более двух ключей

      db.Keys("test", "test2", 0, 2, true)
    
  • не используем префикс ключа, а выбираем по всем

      db.Keys("", "test3", 0, 0, false)
    

Documentation

Overview

Package keystore содержит реализацию простого хранилища данных по ключу (key-value store).

Все данные хранятся в одном файле и не используют отдельного индекса. При первом открытии хранилища происходит построение индекса с содержимым файла и проверка целостности хранилища. Эта операция может занимать некоторое время при открытии действительно больших файлов, поэтому данная библиотека расчитана в первую очередь на небольшие хранилища.

По умолчанию все записи в хранилище заканчиваются вызовом метода os.File.Sync, что позволяет быть до некоторой степени уверенными, что данные при сбое не потеряются. Но, к сожалению, это одновременно сильно замедляет любую операцию записи. Если вы хотите самостоятельно управлять операциями сброса кешей файловой системы, то можно вызвать метод db.SetSync(false) и затем вызывать метод db.Sync вручную из кода.

При удалении или перезаписи значений, свободные участки помечаются специальным образом и в дальнейшем используются повторно, когда в них может уместиться новая запись. Таким образом файл не очень сильно разрастается при большом количестве удалений/вставок новый записей, при условии, что новые значения не превышают по объему удаленные.

При работе с хранилищем поддерживаются групповые операции: db.Gets, db.Puts, db.Deletes. Так же добавлены методы для работы с данными в формате JSON и автоматического преобразования объектов в/из него.

Работа с ключами хранилища и выборки данных поддерживается единственным методом db.Keys, который позволяет достаточно гибко выбирать только те ключи, которые соответствуют заданным критериям. Список ключей всегда возвращается в упорядоченном виде и может быть использован в дальнейшем для выполнения групповых операций.

То, что ключи представлены в виде string не ограничивает вас в использовании бинарных данных. Например, ключ может быть и вида "\x00\x00\x00\xc7". Так что это говорит только о том, что ключ не может динамически изменять свое значение.

Для облегчения работы с данной библиотекой все методы хранилища продублированы в виде глобальных функций, где первым параметром указывается имя файла с хранилищем. Открытые таким образом хранилища кешируются в глобальном списке и могут быть закрыты все сразу вызовом CloseAll.

Example
package main

import (
	"encoding/json"
	"log"
	"os"
	"time"

	"github.com/mdigger/keystore"
)

func main() {
	// автоматически закрыть по окончании все открытые хранилища
	defer keystore.CloseAll()
	var dbname = "db/test.db" // имя файла с хранилищем данных
	// сохраняем данные в хранилище в формате JSON
	// если такого файла с хранилищем не существует, то он будет создан
	// автоматически
	err := keystore.PutsJSON(dbname, map[string]interface{}{
		"t1": "text message",
		"t2": 24,
		"t3": time.Date(1971, time.December, 24, 23, 0, 0, 0, time.UTC),
		"t4": &struct {
			Text string `json:"text"`
		}{
			Text: "test",
		},
	})
	if err != nil {
		log.Fatal("PutsJSON error:", err)
	}
	// выбираем все ключи, сохраненные в хранилище, которые начинаются на `t`
	keys, err := keystore.Keys(dbname, "t", "", 0, 0, true)
	if err != nil {
		log.Fatal("Keys error:", err)
	}
	// получаем список значений из выборки по ключам
	result, err := keystore.GetsJSON(dbname, keys...)
	if err != nil {
		log.Fatal("GetsJSON error:", err)
	}
	// выводим выбранные значения в консоль в виде JSON
	enc := json.NewEncoder(os.Stdout)
	enc.SetIndent("", "  ")
	err = enc.Encode(result)
	if err != nil {
		log.Fatal("JSON encode error:", err)
	}
}
Output:

[
  "text message",
  24,
  "1971-12-24T23:00:00Z",
  {
    "text": "test"
  }
]

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrNotFound = errors.New("key not found")

ErrNotFound возвращается, если данные с таким ключом в хранилище не найдены.

Если вы не хотите отдельно обрабатывать эту ошибку, то можно установить ее значение в nil. Так же иногда бывает удобно переопределить эту ошибку на какую-то другую, более полезную в приложении. Например, для веб-сервера можно переопределить ее в rest.ErrNotFound и тогда не потребуется отдельной проверки на то, что значения с таким ключем нет в хранилище.

Functions

func Bytes

func Bytes(v interface{}) ([]byte, error)

Bytes преобразует данные в бинарный формат с помощью binary.BigEndian. Отдельная обработка добавлена для string, []byte, json.RawMessage, byte и всех остальных, кто поддерживает encoding.BinaryMarshaler, encoding.TextMarshaler, json.Marshaler или fmt.Stringer. Возвращает ошибку, если преобразование не получилось.

func Close

func Close(filename string) error

Close закрывает хранилище с указанным именем. Не возвращает ошибку, если хранилище не было открыто.

func CloseAll

func CloseAll()

CloseAll закрывает все открытые хранилища. Ошибка закрытия хранилищ не обрабатывается.

func Count

func Count(filename string) (uint32, error)

Count возвращает количество записей в хранилище.

func Delete

func Delete(filename, key string) error

Delete удаляет значение с указанным ключом из хранилища.

func Deletes

func Deletes(filename string, keys ...string) error

Deletes удаляет список ключей из хранилища. В отличие от метода Delete, позволяет удалить более одного ключа сразу и не возвращает ошибку при отсуствии ключа в хранилище.

func Get

func Get(filename, key string) ([]byte, error)

Get возвращает данные, сохраненные с указанным ключом. Если данных с таким ключем в хранилище нет, то возвращается ошибка ErrNotFound.

func GetJSON

func GetJSON(filename, key string, v interface{}) error

GetJSON преобразует значение из хранилища в объект. Значение в хранилище должно быть представлено в формате JSON, иначе вернется ошибка.

func Gets

func Gets(filename string, keys ...string) (result [][]byte, err error)

Gets возвращает список значений, соответствующих списку ключей. Игнорирует ошибки с ненайденными ключами: в этом случае в качестве значения для данного ключа будет возвращен nil.

func GetsJSON

func GetsJSON(filename string, keys ...string) (result []json.RawMessage, err error)

GetsJSON возвращает массив значений для указанных ключей в формате json.RawMessage. Возвращает ошибку, если данные не соответствуют формату JSON. Для ненайденных ключей возвращается значение nil.

func Has

func Has(filename, key string) (bool, error)

Has возвращает true, если значение с таким ключом задано в хранилище.

func Keys

func Keys(filename, prefix, last string, offset, limit uint32, asc bool) ([]string, error)

Keys возвращает список ключей, подходящих под запрос.

Подробную информацию по параметрам смотри в описании метода db.Keys.

func NextSequence

func NextSequence(filename string) (uint64, error)

NextSequence возвращает значение счетчика, которое увеличивается при каждом обращении к данной функции хранилища.

func OpenAll

func OpenAll(filenames ...string) error

OpenAll открывает сразу несколько хранилищ с указанными именами файлов. Позволяет провести инициализацию всех используемых хранилищ приложения в одном месте и убедиться, что они существуют или будут созданы.

func Put

func Put(filename, key string, value interface{}) error

Put сохраняет данные в хранилище с указанным ключом. Если данные с таким ключом уже были сохранены в хранилище, то они удаляются и перезаписываются на новые. Значение автоматически преобразуется в формат []byte, используя функцию Bytes.

func PutJSON

func PutJSON(filename, key string, value interface{}) error

PutJSON сохраняет данные в хранилище с указанным ключом в формате JSON. Возвращает ошибку, если не удалось преобразовать объект в формат JSON.

func Puts

func Puts(filename string, values map[string]interface{}) error

Puts позволяет записать сразу несколько значений в хранилище. Для передачи списка данных используется словарь с именем ключа и связанным с ним значением. Для приведения значений к формату []byte используется функция Bytes.

func PutsJSON

func PutsJSON(filename string, values map[string]interface{}) error

PutsJSON сохраняет в хранилище объекты в формате JSON. В случае невозможности представления объекта в виде JSON, возвращается ошибка. При этом те записи, которые на тот момент уже успели записаться, сохраняются.

func Remove

func Remove(filename string) error

Remove удаляет файл с хранилищем с заданным именем, предварительно его закрывая, если оно было открыто.

Types

type DB

type DB struct {
	// contains filtered or unexported fields
}

DB описывает файловое хранилище данных, где значения задаются и выбираются с помощью ключа (key-value store).

func Open

func Open(filename string) (db *DB, err error)

Open возвращает открытую базу с хранилищем в указанном файле. Если база уже была открыта, то повторного открытия не происходит, а возвращается ссылка на ранее открытую.

При первом открытии файла происходит построение индекса ключей и проверка целостности данных, в процессе чего файл читается от начала и до конца. При очень больших файлах данных это может занимать некоторое время, поэтому не рекомендуется использовать эту библиотеку для хранения большого количества данных.

По умолчанию хранилище открывается в синхронном режиме: т.е. любая запись в хранилище приводит к принудительному сбросу данных в файл, что сильно замедляет работу. Если вы хотите самостоятельно управлять процессом сброса кеша или довериться операционной системе, то используйте вызов метода db.SetSync(false).

func (*DB) Close

func (db *DB) Close() error

Close закрывает хранилище. Если специально не задано не выполнять синхронизацию, то при этом происходит принудительный сброс кешей в файл. Повторное выполнение уже закрытого хранилища не приводит к ошибке.

func (*DB) Count

func (db *DB) Count() uint32

Count возвращает количество записей (активных ключей данных) в хранилище.

func (*DB) Delete

func (db *DB) Delete(key string) error

Delete удаляет ключ из хранилища. Если значения с таким ключом в хранилище нет, то возвращается ошибка ErrNotFound.

func (*DB) Deletes

func (db *DB) Deletes(keys ...string) error

Deletes удаляет список ключей из хранилища. В отличие от метода Delete, не возвращает ошибку об отсуствии ключа в хранилище.

func (*DB) Get

func (db *DB) Get(key string) ([]byte, error)

Get возвращает данные, сохраненные с указанным ключом. Если данные с таким ключем в хранилище не сохранены, то возвращается ошибка ErrNotFound и nil в качестве значения. Для пустого значения (nil) всегда возвращается пуcтой массив байт ([]byte{}).

func (*DB) GetJSON

func (db *DB) GetJSON(key string, v interface{}) error

GetJSON преобразует значение из хранилища обратно в объект. Возвращает ошибку, если данные с таким ключем не сохранены или формат сохраненных данных не соответствует формату JSON.

func (*DB) Gets

func (db *DB) Gets(keys ...string) (result [][]byte, err error)

Gets возвращает список значений, соответствующих списку ключей. Игнорирует ошибки с ненайденными ключами: в этом случае в качестве значения для такого ключа будет возвращено nil.

func (*DB) GetsJSON

func (db *DB) GetsJSON(keys ...string) (result []json.RawMessage, err error)

GetsJSON возвращает массив значений для указанных ключей в формате json.RawMessage. Возвращает ошибку, если сохраненные данные не соответствуют формату JSON. Для тех ключей, для которых не задано значение, возвращается nil.

Данную функцию удобно использовать для отдачи результатов выборки в ответ на HTTP-запрос.

func (*DB) Has

func (db *DB) Has(key string) bool

Has возвращает true, если значение с таким ключом определено.

func (*DB) Keys

func (db *DB) Keys(prefix, last string, offset, limit uint32, asc bool) []string

Keys возвращает список ключей, подходящих под запрос.

Для выборки по ключам используется их отсортированный список. Сортировка осуществляется, в первую очередь, по длине ключа, а только потом по алфавиту. Т.е. более короткие ключи имеют больший приоритет. Порядок сортировки задается параметром asc: при значении false сортировка меняется на обратную. Следует обратить на это особое внимание, т.к. данная опция сильно влияет на то, как будет интерпретироваться параметр last.

Если указан prefix, то будут выбраны только те ключи, которые начинаются с этого префикса.

last позволяет выбрать только те ключи, которые идут за или перед индексом (в зависимости от asc) с этим значением (не включая сам элемент last). Ключ со значением last не обязательно должен присутствовать в хранилище: в этом случае просто отбрасывается все до того места, где бы он мог быть в отсортированном списке.

offset задает сдвиг относительно начала списка, а limit - ограничивает количество ключей в выборке.

Example
package main

import (
	"fmt"
	"log"

	"github.com/mdigger/keystore"
)

func main() {
	// открываем хранилище
	db, err := keystore.Open("db/test_keys.db")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close() // закрываем по окончании
	// заносим тестовые данные
	err = db.PutsJSON(map[string]interface{}{
		"test1": 1,
		"test2": 2,
		"test3": 3,
		"test4": 4,
		"test5": 5,
		"aaaa6": 6,
	})
	if err != nil {
		log.Fatal(err)
	}

	// выбираем все ключи, которые начинаются на `test`
	keys := db.Keys("test", "", 0, 0, true)
	fmt.Printf("1: %q\n", keys)
	// выбираем все ключи, которые начинаются на `test`, но после ключа `test2`
	keys = db.Keys("test", "test2", 0, 0, true)
	fmt.Printf("2: %q\n", keys)
	// сортируем вывод в обратном порядке
	keys = db.Keys("", "", 0, 0, false)
	fmt.Printf("3: %q\n", keys)
	// выбираем не более двух ключей
	keys = db.Keys("test", "test2", 0, 2, true)
	fmt.Printf("4: %q\n", keys)
	// не используем префикс ключа, а выбираем по всем
	keys = db.Keys("", "test3", 0, 0, false)
	fmt.Printf("5: %q\n", keys)
}
Output:

1: ["test1" "test2" "test3" "test4" "test5"]
2: ["test3" "test4" "test5"]
3: ["test5" "test4" "test3" "test2" "test1" "aaaa6"]
4: ["test3" "test4"]
5: ["test2" "test1" "aaaa6"]

func (*DB) NextSequence

func (db *DB) NextSequence() (uint64, error)

NextSequence возвращает значение счетчика, которое увеличивается при каждом обращении к данной функции. Обычно используется для задания гарантированного уникального идентификатора записи хранилища, т.к. последнее использованное значение сохраняется в хранилище.

func (*DB) Path

func (db *DB) Path() string

Path возвращает имя файла с хранилищем.

func (*DB) Put

func (db *DB) Put(key string, value []byte) error

Put сохраняет данные в хранилище с указанным ключом. Если данные с таким ключом уже были ранее сохранены в хранилище, то они перезаписываются.

func (*DB) PutJSON

func (db *DB) PutJSON(key string, value interface{}) error

PutJSON сохраняет данные в хранилище с указанным ключом в формате JSON. Возвращает ошибку, если не удалось преобразовать объект в формат JSON.

func (*DB) Puts

func (db *DB) Puts(values map[string][]byte) error

Puts позволяет записать сразу несколько значений в хранилище. Данные передаются в виде связанного списка: ключ - значение. Т.к. ключем в map не может выступать изменяемый массив байт, то значение ключа задается в виде строки.

func (*DB) PutsJSON

func (db *DB) PutsJSON(values map[string]interface{}) error

PutsJSON сохраняет в хранилище объекты в формате JSON. Возвращает ошибку, если не удалось преобразовать объект в формат JSON. При этом те значения, которые на момент ошибки уже были сохранены в хранилище, остаются.

func (*DB) SetSync

func (db *DB) SetSync(sync bool)

SetSync устанавливает значение флага автоматического сброса кеша после каждой записи.

func (*DB) String

func (db *DB) String() string

String возвращает имя файла с хранилища с префиксом "db:" и обычно используется для отладки или вывода в лог имени хранилища.

func (*DB) Sync

func (db *DB) Sync() error

Sync принудительно сбрасывает данные из кеша в файл.

Вызов данного метода обычно не требуется, если вручную не выключен автоматический сброс кешей при любой операции записи.

type Timestamp

type Timestamp struct {
	time.Time
}

Timestamp подменяет представление времени в формате JSON в виде числа.

func Now

func Now() Timestamp

Now возвращает текущее время в виде Timestamp.

func (Timestamp) MarshalJSON

func (t Timestamp) MarshalJSON() ([]byte, error)

MarshalJSON представляет время в формате JSON в виде числа.

func (*Timestamp) UnmarshalJSON

func (t *Timestamp) UnmarshalJSON(b []byte) error

UnmarshalJSON десериализует представление времени из формата JSON.

type UID

type UID uint64

UID представляет из себя уникальный идентификатор, основанный на временной метке и внутреннем счетчике. Его удобно использовать в качестве глобального уникального идентификатора сразу для нескольких хранилищ, так как его значения действительно будут уникальными и монотонно возрастающими, что позволяет использовать сортировку ключей в запросах. В качестве точки отсчета используется дата 2006-01-02T15:04:05Z07:00.

func DateUID

func DateUID(date time.Time) UID

DateUID возвращает уже не совсем уникальный идентификатор для указанных даты и времени, но без учета счетчика. Может использоваться для выборки ключей до или после указанной даты.

Даты до 2006-01-02T15:04:05Z07:00 считаются невалидными и используются как нулевые значения, чтобы не нарушать порядок сортировки.

func NewUID

func NewUID() UID

NewUID возвращает уникальный идентификатор, основанный на времени и внутреннем счетчике. Шесть байт отведено под текущее время, и два байта - под счетчик.

func ParseUID

func ParseUID(s string) UID

ParseUID разбирает уникальный идентификатор из строки. В случае ошибки разбора будет возвращен 0.

func (UID) Byte

func (uid UID) Byte() []byte

Byte возвращает бинарное представление уникального идентификатора.

func (UID) Counter

func (uid UID) Counter() uint16

Counter возвращает значение счетчика уникального идентификатора.

func (UID) MarshalBinary

func (uid UID) MarshalBinary() ([]byte, error)

MarshalBinary возвращает бинарное представление уникального идентификатора.

func (UID) MarshalText

func (uid UID) MarshalText() (text []byte, err error)

MarshalText обеспечивает представление уникального идентификатора в виде текста.

func (UID) String

func (uid UID) String() string

String возвращает строковое представление уникального идентификатора.

func (UID) Time

func (uid UID) Time() time.Time

Time возвращает информацию о времени создания уникального идентификатора.

func (*UID) UnmarshalBinary

func (uid *UID) UnmarshalBinary(data []byte) error

UnmarshalBinary восстанавливает уникальный идентификатор из его бинарного представления.

func (*UID) UnmarshalText

func (uid *UID) UnmarshalText(data []byte) error

UnmarshalText восстанавливает значение уникального идентификатора из текстового представления.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL