Full Code of abronan/valkeyrie for AI

main a5556e0b4245 cached
23 files
60.5 KB
16.8k tokens
64 symbols
1 requests
Download .txt
Repository: abronan/valkeyrie
Branch: main
Commit: a5556e0b4245
Files: 23
Total size: 60.5 KB

Directory structure:
gitextract__0jbl6wy/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   ├── feature_request.yml
│   │   └── new_store.yml
│   ├── dependabot.yml
│   └── workflows/
│       └── build.yml
├── .gitignore
├── .golangci.yml
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── docs/
│   └── examples.md
├── go.mod
├── go.sum
├── maintainers.md
├── mock_test.go
├── readme.md
├── store/
│   ├── errors.go
│   ├── helpers.go
│   └── store.go
├── testsuite/
│   └── suite.go
├── valkeyrie.go
└── valkeyrie_test.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: 🐞 Bug Report
description: Create a report to help us improve.
body:
  - type: checkboxes
    id: terms
    attributes:
      label: Welcome
      options:
        - label: Yes, I've searched similar issues on GitHub and didn't find any.
          required: true
        - label: Yes, I've included all information below (version, config, etc).
          required: true

  - type: textarea
    id: problem
    attributes:
      label: Description of the problem
      placeholder: Your problem description
    validations:
      required: true

  - type: input
    id: version
    attributes:
      label: Version of Valkeyrie
    validations:
      required: true

  - type: textarea
    id: go-env
    attributes:
      label: Go environment
      value: |-
        <details>

        ```console
        $ go version && go env
        # paste output here
        ```

        </details>
    validations:
      required: true

  - type: textarea
    id: code-example
    attributes:
      label: Code example or link to a public repository
      value: |-
        <details>

        ```go
        // add your code here
        ```

        </details>
    validations:
      required: true


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: ❓ Questions
    url: https://github.com/kvtools/valkeyrie/discussions
    about: If you have a question, or are looking for advice, please post on our Discussions forum!



================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: 💡 Feature request
description: "Suggest an idea for this project."
body:

  - type: textarea
    id: problem
    attributes:
      label: Your feature request related to a problem? Please describe.
      placeholder: "A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]"
    validations:
      required: true

  - type: textarea
    id: solution
    attributes:
      label: Describe the solution you'd like.
      placeholder: "A clear and concise description of what you want to happen."
    validations:
      required: true

  - type: textarea
    id: alternatives
    attributes:
      label: Describe alternatives you've considered.
      placeholder: "A clear and concise description of any alternative solutions or features you've considered."
    validations:
      required: true

  - type: textarea
    id: additional
    attributes:
      label: Additional context.
      placeholder: "Add any other context or screenshots about the feature request here."
    validations:
      required: false


================================================
FILE: .github/ISSUE_TEMPLATE/new_store.yml
================================================
name: 🧩 Add a new type of store
description: "Proposal"
body:

  - type: input
    id: store-name
    attributes:
      label: The name of the store.
    validations:
      required: true

  - type: input
    id: repo-url
    attributes:
      label: The URL of your repository.
    validations:
      required: true

  - type: textarea
    id: description
    attributes:
      label: Provide information about the store.
      placeholder: "A clear and concise description."
    validations:
      required: false


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: gomod
    directory: "/"
    schedule:
      interval: monthly
    cooldown:
      default-days: 7
    groups:
      go:
        patterns:
          - "*"  # Group all updates into a single larger pull request.

  - package-ecosystem: github-actions
    directory: "/"
    schedule:
      interval: monthly
    cooldown:
      default-days: 7
    groups:
      github-actions:
        patterns:
          - "*"  # Group all updates into a single larger pull request.


================================================
FILE: .github/workflows/build.yml
================================================
name: Build and test

on: [push, pull_request]

env:
  GOLANGCI_LINT_VERSION: v2.11

permissions: {}

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        go-version: [stable, oldstable]

    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - uses: actions/setup-go@v6
        with:
          go-version: ${{ matrix.go-version }}

      - name: Download and check dependencies
        run: |
          go mod tidy --diff

      - name: Install golangci-lint ${{ env.GOLANGCI_LINT_VERSION }}
        uses: golangci/golangci-lint-action@v9
        with:
          version: ${{ env.GOLANGCI_LINT_VERSION }}

      - name: Run tests
        run: make test


================================================
FILE: .gitignore
================================================
.idea/
.DS_Store
*.iml
dump.rdb


================================================
FILE: .golangci.yml
================================================
version: "2"

formatters:
  enable:
    - gci
    - gofumpt
  settings:
    gofumpt:
      extra-rules: false

linters:
  default: none
  enable:
    - asasalint
    - asciicheck
    - bidichk
    - bodyclose
    - contextcheck
    - depguard
    - dogsled
    - durationcheck
    - err113
    - errcheck
    - errname
    - errorlint
    - forbidigo
    - forcetypeassert
    - funlen
    - gochecknoglobals
    - gochecknoinits
    - gocognit
    - goconst
    - gocritic
    - gocyclo
    - godot
    - godox
    - goheader
    - gomoddirectives
    - gomodguard
    - goprintffuncname
    - gosec
    - govet
    - importas
    - ineffassign
    - makezero
    - misspell
    - nakedret
    - nestif
    - nilerr
    - noctx
    - nolintlint
    - predeclared
    - promlinter
    - revive
    - staticcheck
    - tagliatelle
    - thelper
    - unconvert
    - unparam
    - unused
    - usestdlibvars
    - wastedassign

  settings:
    depguard:
      rules:
        main:
          deny:
            - pkg: github.com/instana/testify
              desc: not allowed
            - pkg: github.com/sirupsen/logrus
              desc: not allowed
            - pkg: github.com/pkg/errors
              desc: Should be replaced by standard lib errors package
    funlen:
      lines: -1
      statements: 45
    goconst:
      min-len: 5
      min-occurrences: 3
    gocritic:
      disabled-checks:
        - unnamedResult
        - sloppyReassign
        - rangeValCopy
        - octalLiteral
        - paramTypeCombine # already handle by gofumpt.extra-rules
      enabled-tags:
        - diagnostic
        - style
        - performance
      settings:
        hugeParam:
          sizeThreshold: 100
    gocyclo:
      min-complexity: 15
    godox:
      keywords:
        - FIXME
    govet:
      disable:
        - fieldalignment
      enable-all: true
    misspell:
      locale: US
    staticcheck:
      checks:
        - all

  exclusions:
    warn-unused: true
    rules:
      - linters:
          - funlen
          - goconst
        path: (.+)_test.go
      - linters:
          - gochecknoglobals
        path: valkeyrie.go

issues:
  max-issues-per-linter: 0
  max-same-issues: 0


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing

All contributions are useful, whether it is a simple typo, a more complex change, or just pointing out an issue.
We welcome any contribution so feel free to take part in the discussions.
If you want to contribute to the project, please make sure to review this document carefully.

- [Setting up the environment](#working-environment)
- [Before submitting a change](#before-submitting-a-change)
- [Your first pull request](#your-first-pull-request)

## Working Environment

### Prerequisites

- Git
- Golang
- One or all of the supported datastore (Zookeeper / Consul / Etcd / Redis / BoltDB)

### Installing Golang

Install golang using your favorite package manager on Linux or with the archive
following these [Guidelines](https://golang.org/doc/install).

An easy way to get started on macOS is to use [homebrew](https://brew.sh) and type
`brew install go` in a shell.

### Local testing of key/value stores

In addition to installing golang, you will need to install some or all of the key value stores for testing.

Refer to each of these stores documentation in order to proceed with installation.
Generally, the tests are using the **default configuration** with the **default port** to connect to a store and run the test suite.

To test a change, you can run the test suite with the following command:

```bash
make test
```

## Before submitting a change

Make sure you check each of these items before you submit a pull request to avoid many unnecessary back and forth in GitHub comments
(and will help us review and include the change as soon as possible):

- **Open an issue** to clearly state the problem.
This will be helpful to keep track of what needs to be fixed. This also helps sort and prioritize issues.

- **Run the following command**: `make validate`, to ensure that your code is properly formatted.

- **For non-trivial changes, write a test**: this is to ensure that we don't encounter any regression in the future.

- **Write a complete description** for your pull request (avoid using `-m` flag when committing a change unless it is a trivial one).

- **Sign-off your commits** using the `-s` flag (you can configure an alias to `git commit` adding `-s` for convenience).

- **Squash your commits** if the pull requests includes many commits that are related.
This is to maintain a clean history of the change and better identify faulty commits if reverting a change is ever needed.
We will tell you if squashing your commits is necessary.

- **If the change is solving one or more issues listed on the repository**:
you can reference the issue in your comment with `closes #XXX` or `fixes #XXX`.
This will automatically close the related issues on merging the change.
See [GitHub documentation](https://help.github.com/articles/closing-issues-using-keywords/) for more details.

Finally, submit your *Pull Request*.

## Your first Pull Request

You made it to your first Pull Request? It's only the start of the process.
Following steps may include a discussion on the design and tradeoffs of your proposed solution.

Additionally, there will be a *code review process* to find out potential bugs.
Part of being a helpful community is to make sure we point out improvements
and deliver actionable items to work towards fixing potential issues.
Feel free to ask questions if you are stuck, so we can help you.

*Don't be discouraged* if your change happens not to be included.
All contributions are helpful in a way.
Your PR most certainly made the discussion go forward in many aspects
and helped to work towards our common goal of making the project better for everyone.

**Welcome!**


================================================
FILE: LICENSE
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   Copyright 2014-2016 Docker, Inc.
   Copyright 2021-2022 Valkeyrie 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

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: Makefile
================================================
.PHONY: all
all: validate test

## Run validates
.PHONY: validate
validate:
	golangci-lint run

## Run tests
.PHONY: test
test:
	go test -v -race ./...


================================================
FILE: docs/examples.md
================================================
# Examples

This document contains useful example of usage for `valkeyrie`.
It might not be complete but provides with general information on how to use the client.

## Create a Store and Use `Put`/`Get`/`Delete`

```go
package main

import (
	"context"
	"log"
	"time"

	"github.com/kvtools/consul"
	"github.com/kvtools/valkeyrie"
)

func main() {
	ctx := context.Background()

	config := &consul.Config{
		ConnectionTimeout: 10 * time.Second,
	}

	kv, err := valkeyrie.NewStore(ctx, consul.StoreName, []string{"localhost:8500"}, config)
	if err != nil {
		log.Fatal("Cannot create store consul")
	}

	key := "foo"

	err = kv.Put(ctx, key, []byte("bar"), nil)
	if err != nil {
		log.Fatalf("Error trying to put value at key: %v", key)
	}

	pair, err := kv.Get(ctx, key, nil)
	if err != nil {
		log.Fatalf("Error trying accessing value at key: %v", key)
	}

	err = kv.Delete(ctx, key)
	if err != nil {
		log.Fatalf("Error trying to delete key %v", key)
	}

	log.Printf("value: %s", string(pair.Value))
}
```

## List Keys

```go
// List will list all the keys under `key` if it contains a set of child keys/values
entries, err := kv.List(ctx, key, nil)
for _, pair := range entries {
    fmt.Printf("key=%v - value=%v", pair.Key, string(pair.Value))
}
```

## Watching for Events on a Single Key (`Watch`)

You can use watches to watch modifications on a key.
First you need to check if the key exists.
If this is not the case, we need to create it using the `Put` function.

```go
package example

import (
	"context"
	"fmt"

	"github.com/kvtools/valkeyrie/store"
)

func watch(ctx context.Context, kv store.Store, key string) error {
	// Checking on the key before watching
	exists, _ := kv.Exists(ctx, key, nil)
	if !exists {
		err := kv.Put(ctx, key, []byte("bar"), nil)
		if err != nil {
			return fmt.Errorf("something went wrong when initializing key %v", key)
		}
	}

	events, err := kv.Watch(ctx, key, nil)
	if err != nil {
		return err
	}

	for {
		select {
		case pair := <-events:
			// Do something with events
			fmt.Printf("value changed on key %v: new value=%v", key, pair.Value)
		}
	}
	// ...

	return nil
}
```

## Watching for Events Happening on Child Keys (`WatchTree`)

You can use watches to watch modifications on a key.
First you need to check if the key exists.
If this is not the case, we need to create it using the `Put` function.

There is a special step here if you are using etcd **APIv2** and if you want your code to work across backends.
`etcd` with **APIv2** makes the distinction between directories and keys,
we need to make sure that the created key is considered as a directory by enforcing `IsDir` at `true`.

```go
package example

import (
	"context"
	"fmt"

	"github.com/kvtools/valkeyrie/store"
)

func watchTree(ctx context.Context, kv store.Store, key string) error {
	// Checking on the key before watching
	exists, _ := kv.Exists(ctx, key, nil)
	if !exists {
		// Do not forget `IsDir:true` if you are using etcd APIv2
		err := kv.Put(ctx, key, []byte("bar"), &store.WriteOptions{IsDir: true})
		if err != nil {
			return fmt.Errorf("something went wrong when initializing key %v", key)
		}
	}

	events, err := kv.WatchTree(ctx, key, nil)
	if err != nil {
		return err
	}

	select {
	case pairs := <-events:
		// Do something with events
		for _, pair := range pairs {
			fmt.Printf("value changed on key %v: new value=%v", key, pair.Value)
		}
	}

	// ...

	return nil
}
```

## Distributed Locking, Using Lock/Unlock

```go
package example

import (
	"context"
	"fmt"
	"time"

	"github.com/kvtools/valkeyrie/store"
)

func foo(ctx context.Context, kv store.Store) error {
	key := "lockKey"
	value := []byte("bar")

	// Initialize a distributed lock. TTL is optional, it is here to make sure that
	// the lock is released after the program that is holding the lock ends or crashes
	lock, err := kv.NewLock(key, &store.LockOptions{Value: value, TTL: 2 * time.Second})
	if err != nil {
		return fmt.Errorf("something went wrong when trying to initialize the Lock")
	}

	// Try to lock the key, the call to Lock() is blocking
	_, err = lock.Lock(nil)
	if err != nil {
		return fmt.Errorf("something went wrong when trying to lock key %v", key)
	}

	// Get should work because we are holding the key
	pair, err := kv.Get(ctx, key, nil)
	if err != nil {
		return fmt.Errorf("key %v has value %v", key, pair.Value)
	}

	// Unlock the key
	err = lock.Unlock(ctx)
	if err != nil {
		return fmt.Errorf("something went wrong when trying to unlock key %v", key)
	}

	// ...

	return nil
}
```


================================================
FILE: go.mod
================================================
module github.com/kvtools/valkeyrie

go 1.22

require github.com/stretchr/testify v1.11.1

require (
	github.com/davecgh/go-spew v1.1.1 // indirect
	github.com/kr/pretty v0.3.1 // indirect
	github.com/pmezard/go-difflib v1.0.0 // indirect
	gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
	gopkg.in/yaml.v3 v3.0.1 // indirect
)


================================================
FILE: go.sum
================================================
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=


================================================
FILE: maintainers.md
================================================
- [Alexandre Beslic](https://github.com/abronan)
- [Victor Castell](https://github.com/victorcoder)
- [Nicolas Mengin](https://github.com/nmengin)
- [Maciej Winnicki](https://github.com/mthenw)
- [Ludovic Fernandez](https://github.com/ldez)
- [Kevin Pollet](https://github.com/kevinpollet)
- [Tom Moulard](https://github.com/tomMoulard)


================================================
FILE: mock_test.go
================================================
package valkeyrie

import (
	"context"

	"github.com/kvtools/valkeyrie/store"
)

const testStoreName = "mock"

func newStore(ctx context.Context, endpoints []string, options Config) (store.Store, error) {
	cfg, ok := options.(*Config)
	if !ok && options != nil {
		return nil, &store.InvalidConfigurationError{Store: testStoreName, Config: options}
	}

	return New(ctx, endpoints, cfg)
}

type Mock struct {
	cfg *Config
}

// New creates a new Mock client.
//
//nolint:gocritic
func New(_ context.Context, _ []string, cfg *Config) (*Mock, error) {
	return &Mock{cfg: cfg}, nil
}

func (m Mock) Put(_ context.Context, _ string, _ []byte, _ *store.WriteOptions) error {
	panic("implement me")
}

func (m Mock) Get(_ context.Context, _ string, _ *store.ReadOptions) (*store.KVPair, error) {
	panic("implement me")
}

func (m Mock) Delete(_ context.Context, _ string) error {
	panic("implement me")
}

func (m Mock) Exists(_ context.Context, _ string, _ *store.ReadOptions) (bool, error) {
	panic("implement me")
}

func (m Mock) Watch(_ context.Context, _ string, _ *store.ReadOptions) (<-chan *store.KVPair, error) {
	panic("implement me")
}

func (m Mock) WatchTree(_ context.Context, _ string, _ *store.ReadOptions) (<-chan []*store.KVPair, error) {
	panic("implement me")
}

func (m Mock) NewLock(_ context.Context, _ string, _ *store.LockOptions) (store.Locker, error) {
	panic("implement me")
}

func (m Mock) List(_ context.Context, _ string, _ *store.ReadOptions) ([]*store.KVPair, error) {
	panic("implement me")
}

func (m Mock) DeleteTree(_ context.Context, _ string) error {
	panic("implement me")
}

func (m Mock) AtomicPut(_ context.Context, _ string, _ []byte, _ *store.KVPair, _ *store.WriteOptions) (bool, *store.KVPair, error) {
	panic("implement me")
}

func (m Mock) AtomicDelete(_ context.Context, _ string, _ *store.KVPair) (bool, error) {
	panic("implement me")
}

func (m Mock) Close() error {
	panic("implement me")
}


================================================
FILE: readme.md
================================================
<p align="center">
  <img alt="golangci-lint logo" src="docs/valkeyrie.png" height="350" />
  <h3 align="center">Valkeyrie</h3>
  <p align="center">Distributed Key/Value Store Abstraction Library</p>
</p>

# Valkeyrie

[![Go Reference](https://pkg.go.dev/badge/github.com/kvtools/valkeyrie.svg)](https://pkg.go.dev/github.com/kvtools/valkeyrie)
[![Build and test](https://github.com/kvtools/valkeyrie/actions/workflows/build.yml/badge.svg)](https://github.com/kvtools/valkeyrie/actions/workflows/build.yml)
[![Go Report Card](https://goreportcard.com/badge/github.com/kvtools/valkeyrie)](https://goreportcard.com/report/github.com/kvtools/valkeyrie)

`valkeyrie` provides a Go native library to store metadata using Distributed Key/Value stores (or common databases).

Its goal is to abstract common store operations (`Get`, `Put`, `List`, etc.) for multiple Key/Value store backends.

For example, you can easily implement a generic *Leader Election* algorithm on top of it (see the [docker/leadership](https://github.com/docker/leadership) repository).

The benefit of `valkeyrie` is not to duplicate the code for programs that should support multiple distributed Key/Value stores such as `Consul`/`etcd`/`zookeeper`, etc.

## Examples of Usage

You can refer to [Examples](https://github.com/kvtools/valkeyrie/blob/master/docs/examples.md) for a basic overview of the library.

```go
package main

import (
	"context"
	"log"
	"time"

	"github.com/kvtools/consul"
	"github.com/kvtools/valkeyrie"
)

func main() {
	ctx := context.Background()
	
	config := &consul.Config{
		ConnectionTimeout: 10 * time.Second,
	}

	kv, err := valkeyrie.NewStore(ctx, consul.StoreName, []string{"localhost:8500"}, config)
	if err != nil {
		log.Fatal("Cannot create store consul")
	}

	key := "foo"
	

	err = kv.Put(ctx, key, []byte("bar"), nil)
	if err != nil {
		log.Fatalf("Error trying to put value at key: %v", key)
	}

	pair, err := kv.Get(ctx, key, nil)
	if err != nil {
		log.Fatalf("Error trying accessing value at key: %v", key)
	}

	log.Printf("value: %s", string(pair.Value))

	err = kv.Delete(ctx, key)
	if err != nil {
		log.Fatalf("Error trying to delete key %v", key)
	}
}
```

## Compatibility

A **storage backend** in `valkeyrie` implements (fully or partially) the [Store](https://github.com/kvtools/valkeyrie/blob/master/store/store.go#L69) interface.

| Calls                 | Consul | Etcd | Zookeeper | Redis | BoltDB | DynamoDB |
|-----------------------|:------:|:----:|:---------:|:-----:|:------:|:--------:|
| Put                   |  🟢️   | 🟢️  |    🟢️    |  🟢️  |  🟢️   |   🟢️    |
| Get                   |  🟢️   | 🟢️  |    🟢️    |  🟢️  |  🟢️   |   🟢️    |
| Delete                |  🟢️   | 🟢️  |    🟢️    |  🟢️  |  🟢️   |   🟢️    |
| Exists                |  🟢️   | 🟢️  |    🟢️    |  🟢️  |  🟢️   |   🟢️    |
| Watch                 |  🟢️   | 🟢️  |    🟢️    |  🟢️  |   🔴   |    🔴    |
| WatchTree             |  🟢️   | 🟢️  |    🟢️    |  🟢️  |   🔴   |    🔴    |
| NewLock (Lock/Unlock) |  🟢️   | 🟢️  |    🟢️    |  🟢️  |   🔴   |   🟢️    |
| List                  |  🟢️   | 🟢️  |    🟢️    |  🟢️  |  🟢️   |   🟢️    |
| DeleteTree            |  🟢️   | 🟢️  |    🟢️    |  🟢️  |  🟢️   |   🟢️    |
| AtomicPut             |  🟢️   | 🟢️  |    🟢️    |  🟢️  |  🟢️   |   🟢️    |
| AtomicDelete          |  🟢️   | 🟢️  |    🟢️    |  🟢️  |  🟢️   |   🟢️    |

The store implementations:

- [boltdb](https://github.com/kvtools/boltdb)
- [consul](https://github.com/kvtools/consul)
- [dynamodb](https://github.com/kvtools/dynamodb)
- [etcdv2](https://github.com/kvtools/etcdv2)
- [etcdv3](https://github.com/kvtools/etcdv3)
- [redis](https://github.com/kvtools/redis)
- [zookeeper](https://github.com/kvtools/zookeeper)

The store template:

- [template](https://github.com/kvtools/template)

## Limitations

Distributed Key/Value stores often have different concepts for managing and formatting keys and their associated values.
Even though `valkeyrie` tries to abstract those stores aiming for some consistency, in some cases it can't be applied easily.

Calls like `WatchTree` may return different events (or number of events) depending on the backend
(for now, `Etcd` and `Consul` will likely return more events than `Zookeeper` that you should triage properly).

## Contributing

Want to contribute to `valkeyrie`?
Take a look at the [Contribution Guidelines](https://github.com/kvtools/valkeyrie/blob/master/CONTRIBUTING.md).

The [Maintainers](https://github.com/kvtools/valkeyrie/blob/master/maintainers.md).

## Copyright and License

Apache License Version 2.0

Valkeyrie started as a hard fork of the unmaintained [libkv](https://github.com/docker/libkv).


================================================
FILE: store/errors.go
================================================
package store

import (
	"errors"
	"fmt"
)

var (
	// ErrCallNotSupported is thrown when a method is not implemented/supported by the current backend.
	ErrCallNotSupported = errors.New("the current call is not supported with this backend")
	// ErrNotReachable is thrown when the API cannot be reached for issuing common store operations.
	ErrNotReachable = errors.New("api not reachable")
	// ErrCannotLock is thrown when there is an error acquiring a lock on a key.
	ErrCannotLock = errors.New("error acquiring the lock")
	// ErrKeyModified is thrown during an atomic operation if the index does not match the one in the store.
	ErrKeyModified = errors.New("unable to complete atomic operation, key modified")
	// ErrKeyNotFound is thrown when the key is not found in the store during a Get operation.
	ErrKeyNotFound = errors.New("key not found in store")
	// ErrPreviousNotSpecified is thrown when the previous value is not specified for an atomic operation.
	ErrPreviousNotSpecified = errors.New("previous K/V pair should be provided for the Atomic operation")
	// ErrKeyExists is thrown when the previous value exists in the case of an AtomicPut.
	ErrKeyExists = errors.New("previous K/V pair exists, cannot complete Atomic operation")
)

// InvalidConfigurationError is thrown when the type of the configuration is not supported by a store.
type InvalidConfigurationError struct {
	Store  string
	Config any
}

func (e *InvalidConfigurationError) Error() string {
	return fmt.Sprintf("%s: invalid configuration type: %T", e.Store, e.Config)
}

// UnknownConstructorError is thrown when a requested store is not register.
type UnknownConstructorError struct {
	Store string
}

func (e UnknownConstructorError) Error() string {
	return fmt.Sprintf("unknown constructor %q (forgotten import?)", e.Store)
}


================================================
FILE: store/helpers.go
================================================
package store

import (
	"strings"
)

// CreateEndpoints creates a list of endpoints given the right scheme.
func CreateEndpoints(addrs []string, scheme string) (entries []string) {
	for _, addr := range addrs {
		entries = append(entries, scheme+"://"+addr)
	}

	return entries
}

// SplitKey splits the key to extract path information.
func SplitKey(key string) (path []string) {
	if strings.Contains(key, "/") {
		return strings.Split(key, "/")
	}

	return []string{key}
}


================================================
FILE: store/store.go
================================================
// Package store contains KV store backends.
package store

import (
	"context"
	"time"
)

// Store represents the backend K/V storage.
// Each store should support every call listed here.
// Or it couldn't be implemented as a K/V backend for valkeyrie.
type Store interface {
	// Put a value at the specified key.
	Put(ctx context.Context, key string, value []byte, opts *WriteOptions) error

	// Get a value given its key.
	Get(ctx context.Context, key string, opts *ReadOptions) (*KVPair, error)

	// Delete the value at the specified key.
	Delete(ctx context.Context, key string) error

	// Exists Verify if a Key exists in the store.
	Exists(ctx context.Context, key string, opts *ReadOptions) (bool, error)

	// Watch for changes on a key.
	Watch(ctx context.Context, key string, opts *ReadOptions) (<-chan *KVPair, error)

	// WatchTree watches for changes on child nodes under a given directory.
	WatchTree(ctx context.Context, directory string, opts *ReadOptions) (<-chan []*KVPair, error)

	// NewLock creates a lock for a given key.
	// The returned Locker is not held and must be acquired with `.Lock`.
	// The Value is optional.
	NewLock(ctx context.Context, key string, opts *LockOptions) (Locker, error)

	// List the content of a given prefix.
	List(ctx context.Context, directory string, opts *ReadOptions) ([]*KVPair, error)

	// DeleteTree deletes a range of keys under a given directory.
	DeleteTree(ctx context.Context, directory string) error

	// AtomicPut Atomic CAS operation on a single value.
	// Pass previous = nil to create a new key.
	AtomicPut(ctx context.Context, key string, value []byte, previous *KVPair, opts *WriteOptions) (bool, *KVPair, error)

	// AtomicDelete Atomic delete of a single value.
	AtomicDelete(ctx context.Context, key string, previous *KVPair) (bool, error)

	// Close the store connection.
	Close() error
}

// KVPair represents {Key, Value, LastIndex} tuple.
type KVPair struct {
	Key       string
	Value     []byte
	LastIndex uint64
}

// WriteOptions contains optional request parameters.
type WriteOptions struct {
	IsDir bool
	TTL   time.Duration

	// If true, the client will keep the lease alive in the background
	// for stores that are allowing it.
	KeepAlive bool
}

// ReadOptions contains optional request parameters.
type ReadOptions struct {
	// Consistent defines if the behavior of a Get operation is linearizable or not.
	// Linearizability allows us to 'see' objects based on a real-time total order
	// as opposed to an arbitrary order or with stale values ('inconsistent' scenario).
	Consistent bool
}

// LockOptions contains optional request parameters.
type LockOptions struct {
	Value          []byte        // Optional, value to associate with the lock.
	TTL            time.Duration // Optional, expiration ttl associated with the lock.
	RenewLock      chan struct{} // Optional, chan used to control and stop the session ttl renewal for the lock.
	DeleteOnUnlock bool          // If true, the value will be deleted when the lock is unlocked or expires.
}

// Locker provides locking mechanism on top of the store.
// Similar to sync.Locker except it may return errors.
type Locker interface {
	Lock(ctx context.Context) (<-chan struct{}, error)
	Unlock(ctx context.Context) error
}


================================================
FILE: testsuite/suite.go
================================================
// Package testsuite the valkeyrie tests suite.
package testsuite

import (
	"context"
	"strconv"
	"strings"
	"testing"
	"time"

	"github.com/kvtools/valkeyrie/store"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

const testTimeout = 60 * time.Second

// RunTestCommon tests the minimal required APIs which
// should be supported by all K/V backends.
func RunTestCommon(t *testing.T, kv store.Store) {
	t.Helper()

	testPutGetDeleteExists(t, kv)
	testList(t, kv)
	testDeleteTree(t, kv)
}

// RunTestListLock tests the list output for mutexes
// and checks that internal side keys are not listed.
func RunTestListLock(t *testing.T, kv store.Store) {
	t.Helper()

	testListLockKey(t, kv)
}

// RunTestAtomic tests the Atomic operations by the K/V
// backends.
func RunTestAtomic(t *testing.T, kv store.Store) {
	t.Helper()

	testAtomicPut(t, kv)
	testAtomicPutCreate(t, kv)
	testAtomicPutWithSlashSuffixKey(t, kv)
	testAtomicDelete(t, kv)
}

// RunTestWatch tests the watch/monitor APIs supported
// by the K/V backends.
func RunTestWatch(t *testing.T, kv store.Store) {
	t.Helper()

	testWatch(t, kv)
	testWatchTree(t, kv)
}

// RunTestLock tests the KV pair Lock/Unlock APIs supported
// by the K/V backends.
func RunTestLock(t *testing.T, kv store.Store) {
	t.Helper()

	testLockUnlock(t, kv)
}

// RunTestLockTTL tests the KV pair Lock with TTL APIs supported
// by the K/V backends.
func RunTestLockTTL(t *testing.T, kv store.Store, backup store.Store) {
	t.Helper()

	testLockTTL(t, kv, backup)
}

// RunTestTTL tests the TTL functionality of the K/V backend.
func RunTestTTL(t *testing.T, kv store.Store, backup store.Store) {
	t.Helper()

	testPutTTL(t, kv, backup)
}

func checkPairNotNil(t *testing.T, pair *store.KVPair) {
	t.Helper()

	require.NotNilf(t, pair, "pair is nil")
	require.NotNilf(t, pair.Value, "value is nil")
}

func testPutGetDeleteExists(t *testing.T, kv store.Store) {
	t.Helper()

	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
	defer cancel()

	// Get a not exist key should return ErrKeyNotFound.
	_, err := kv.Get(ctx, "testPutGetDelete_not_exist_key", nil)
	assert.ErrorIs(t, err, store.ErrKeyNotFound)

	value := []byte("bar")
	for _, key := range []string{
		"testPutGetDeleteExists",
		"testPutGetDeleteExists/",
		"testPutGetDeleteExists/testbar/",
		"testPutGetDeleteExists/testbar/testfoobar",
	} {
		// Put the key.
		err = kv.Put(ctx, key, value, nil)
		require.NoError(t, err)

		// Get should return the value and an incremented index.
		pair, err := kv.Get(ctx, key, nil)
		require.NoError(t, err)
		checkPairNotNil(t, pair)
		assert.Equal(t, pair.Value, value)
		assert.NotEqual(t, pair.LastIndex, 0)

		// Exists should return true.
		exists, err := kv.Exists(ctx, key, nil)
		require.NoError(t, err)
		assert.True(t, exists)

		// Delete the key.
		err = kv.Delete(ctx, key)
		require.NoError(t, err)

		// Get should fail.
		pair, err = kv.Get(ctx, key, nil)
		assert.Error(t, err)
		assert.Nil(t, pair)

		// Exists should return false.
		exists, err = kv.Exists(ctx, key, nil)
		require.NoError(t, err)
		assert.False(t, exists)
	}
}

func testWatch(t *testing.T, kv store.Store) {
	t.Helper()

	key := "testWatch"
	value := []byte("world")
	newValue := []byte("world!")

	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
	t.Cleanup(cancel)

	// Put the key.
	err := kv.Put(ctx, key, value, nil)
	require.NoError(t, err)

	events, err := kv.Watch(ctx, key, nil)
	require.NoError(t, err)
	require.NotNil(t, events)

	// Update loop.
	go func() {
		timeout := time.After(1 * time.Second)
		tick := time.NewTicker(250 * time.Millisecond)
		defer tick.Stop()
		for {
			select {
			case <-timeout:
				return
			case <-tick.C:
				err := kv.Put(ctx, key, newValue, nil)
				if assert.NoError(t, err) {
					continue
				}
				return
			}
		}
	}()

	// Check for updates.
	eventCount := 1
	for {
		select {
		case event := <-events:
			assert.NotNil(t, event)
			assert.Equal(t, event.Key, key)
			if eventCount == 1 {
				assert.Equal(t, event.Value, value)
			} else {
				assert.Equal(t, event.Value, newValue)
			}
			eventCount++
			// We received all the events we wanted to check.
			if eventCount >= 4 {
				return
			}
		case <-time.After(4 * time.Second):
			t.Fatal("Timeout reached")
			return
		}
	}
}

func testWatchTree(t *testing.T, kv store.Store) {
	t.Helper()

	dir := "testWatchTree"

	node1 := "testWatchTree/node1"
	value1 := []byte("node1")

	node2 := "testWatchTree/node2"
	value2 := []byte("node2")

	node3 := "testWatchTree/node3"
	value3 := []byte("node3")

	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
	t.Cleanup(cancel)

	err := kv.Put(ctx, node1, value1, nil)
	require.NoError(t, err)
	err = kv.Put(ctx, node2, value2, nil)
	require.NoError(t, err)
	err = kv.Put(ctx, node3, value3, nil)
	require.NoError(t, err)

	events, err := kv.WatchTree(ctx, dir, nil)
	require.NoError(t, err)
	require.NotNil(t, events)

	// Update loop.
	go func() {
		time.Sleep(500 * time.Millisecond)

		err := kv.Delete(ctx, node3)
		require.NoError(t, err)
	}()

	// Check for updates.
	eventCount := 1
	for {
		select {
		case event := <-events:
			assert.NotNil(t, event)
			// We received the Delete event on a child node
			// Exit test successfully.
			if eventCount == 2 {
				return
			}
			eventCount++
		case <-time.After(4 * time.Second):
			t.Fatal("Timeout reached")
			return
		}
	}
}

func testAtomicPut(t *testing.T, kv store.Store) {
	t.Helper()

	key := "testAtomicPut"
	value := []byte("world")

	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
	defer cancel()

	// Put the key.
	err := kv.Put(ctx, key, value, nil)
	require.NoError(t, err)

	// Get should return the value and an incremented index.
	pair, err := kv.Get(ctx, key, nil)
	require.NoError(t, err)
	checkPairNotNil(t, pair)
	assert.Equal(t, pair.Value, value)
	assert.NotEqual(t, pair.LastIndex, 0)

	// This CAS should fail: previous exists.
	success, _, err := kv.AtomicPut(ctx, key, []byte("WORLD"), nil, nil)
	assert.Error(t, err)
	assert.False(t, success)

	// This CAS should succeed.
	success, _, err = kv.AtomicPut(ctx, key, []byte("WORLD"), pair, nil)
	require.NoError(t, err)
	assert.True(t, success)

	// This CAS should fail, key has wrong index.
	pair.LastIndex = 6744
	success, _, err = kv.AtomicPut(ctx, key, []byte("WORLDWORLD"), pair, nil)
	assert.Equal(t, err, store.ErrKeyModified)
	assert.False(t, success)
}

func testAtomicPutCreate(t *testing.T, kv store.Store) {
	t.Helper()

	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
	defer cancel()

	// Use a key in a new directory to ensure Stores will create directories
	// that don't yet exist.
	key := "testAtomicPutCreate/create"
	value := []byte("putcreate")

	// AtomicPut the key, previous = nil indicates create.
	success, _, err := kv.AtomicPut(ctx, key, value, nil, nil)
	require.NoError(t, err)
	assert.True(t, success)

	// Get should return the value and an incremented index.
	pair, err := kv.Get(ctx, key, nil)
	require.NoError(t, err)
	checkPairNotNil(t, pair)
	assert.Equal(t, pair.Value, value)

	// Attempting to create again should fail.
	success, _, err = kv.AtomicPut(ctx, key, value, nil, nil)
	assert.ErrorIs(t, err, store.ErrKeyExists)
	assert.False(t, success)

	// This CAS should succeed, since it has the value from Get().
	success, _, err = kv.AtomicPut(ctx, key, []byte("PUTCREATE"), pair, nil)
	require.NoError(t, err)
	assert.True(t, success)
}

func testAtomicPutWithSlashSuffixKey(t *testing.T, kv store.Store) {
	t.Helper()

	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
	defer cancel()

	k1 := "testAtomicPutWithSlashSuffixKey/key/"
	success, _, err := kv.AtomicPut(ctx, k1, []byte{}, nil, nil)
	require.NoError(t, err)
	assert.True(t, success)
}

func testAtomicDelete(t *testing.T, kv store.Store) {
	t.Helper()

	key := "testAtomicDelete"
	value := []byte("world")

	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
	defer cancel()

	// Put the key.
	err := kv.Put(ctx, key, value, nil)
	require.NoError(t, err)

	// Get should return the value and an incremented index.
	pair, err := kv.Get(ctx, key, nil)
	require.NoError(t, err)
	checkPairNotNil(t, pair)
	assert.Equal(t, pair.Value, value)
	assert.NotEqual(t, pair.LastIndex, 0)

	tempIndex := pair.LastIndex

	// AtomicDelete should fail.
	pair.LastIndex = 6744
	success, err := kv.AtomicDelete(ctx, key, pair)
	assert.Error(t, err)
	assert.False(t, success)

	// AtomicDelete should succeed.
	pair.LastIndex = tempIndex
	success, err = kv.AtomicDelete(ctx, key, pair)
	require.NoError(t, err)
	assert.True(t, success)

	// Delete a non-existent key; should fail.
	success, err = kv.AtomicDelete(ctx, key, pair)
	assert.ErrorIs(t, err, store.ErrKeyNotFound)
	assert.False(t, success)
}

func testLockUnlock(t *testing.T, kv store.Store) {
	t.Helper()

	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
	defer cancel()

	key := "testLockUnlock"
	value := []byte("bar")

	// We should be able to create a new lock on key.
	lock, err := kv.NewLock(ctx, key, &store.LockOptions{
		Value:          value,
		TTL:            2 * time.Second,
		DeleteOnUnlock: true,
	})
	require.NoError(t, err)
	require.NotNil(t, lock)

	// Lock should successfully succeed or block.
	lockChan, err := lock.Lock(ctx)
	require.NoError(t, err)
	assert.NotNil(t, lockChan)

	// Get should work.
	pair, err := kv.Get(ctx, key, nil)
	require.NoError(t, err)
	checkPairNotNil(t, pair)
	assert.Equal(t, pair.Value, value)
	assert.NotEqual(t, pair.LastIndex, 0)

	// Unlock should succeed.
	err = lock.Unlock(ctx)
	require.NoError(t, err)

	// Lock should succeed again.
	lockChan, err = lock.Lock(ctx)
	require.NoError(t, err)
	assert.NotNil(t, lockChan)

	// Get should work.
	pair, err = kv.Get(ctx, key, nil)
	require.NoError(t, err)
	checkPairNotNil(t, pair)
	assert.Equal(t, pair.Value, value)
	assert.NotEqual(t, pair.LastIndex, 0)

	err = lock.Unlock(ctx)
	require.NoError(t, err)
}

func testLockTTL(t *testing.T, kv store.Store, otherConn store.Store) {
	t.Helper()

	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
	defer cancel()

	key := "testLockTTL"
	value := []byte("bar")

	renewCh := make(chan struct{})

	// We should be able to create a new lock on key.
	lockOC, err := otherConn.NewLock(ctx, key, &store.LockOptions{
		Value:     value,
		TTL:       2 * time.Second,
		RenewLock: renewCh,
	})
	require.NoError(t, err)
	require.NotNil(t, lockOC)

	// Lock should successfully succeed.
	lockChan, err := lockOC.Lock(ctx)
	require.NoError(t, err)
	assert.NotNil(t, lockChan)

	// Get should work.
	pair, err := otherConn.Get(ctx, key, nil)
	require.NoError(t, err)
	checkPairNotNil(t, pair)
	assert.Equal(t, pair.Value, value)
	assert.NotEqual(t, pair.LastIndex, 0)

	time.Sleep(3 * time.Second)

	value = []byte("foobar")

	// Create a new lock with another connection.
	lock, err := kv.NewLock(ctx, key, &store.LockOptions{
		Value: value,
		TTL:   3 * time.Second,
	})
	require.NoError(t, err)
	require.NotNil(t, lock)

	ctxLock, cancelLock := context.WithTimeout(ctx, 4*time.Second)
	defer cancelLock()

	// Lock should block, the session on the lock
	// is still active and renewed periodically.
	lockChan, _ = lock.Lock(ctxLock)
	require.Nil(t, lockChan)

	// Close the connection.
	_ = otherConn.Close()

	// Force to stop the session renewal for the lock.
	close(renewCh)

	// Let the session on the lock expire.
	time.Sleep(3 * time.Second)

	// Lock should now succeed for the other client.
	locked := make(chan struct{})
	go func(<-chan struct{}) {
		lockChan, err = lock.Lock(ctx)
		require.NoError(t, err)
		assert.NotNil(t, lockChan)
		locked <- struct{}{}
	}(locked)

	select {
	case <-locked:
		break
	case <-time.After(4 * time.Second):
		t.Fatal("Unable to take the lock, timed out")
	}

	// Get should work with the new value.
	pair, err = kv.Get(ctx, key, nil)
	require.NoError(t, err)
	checkPairNotNil(t, pair)
	assert.Equal(t, pair.Value, value)
	assert.NotEqual(t, pair.LastIndex, 0)

	err = lock.Unlock(ctx)
	require.NoError(t, err)
}

func testPutTTL(t *testing.T, kv store.Store, otherConn store.Store) {
	t.Helper()

	firstKey := "testPutTTL"
	firstValue := []byte("foo")

	secondKey := "second"
	secondValue := []byte("bar")

	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
	defer cancel()

	// Put the first key with the Ephemeral flag.
	err := otherConn.Put(ctx, firstKey, firstValue, &store.WriteOptions{TTL: 2 * time.Second})
	require.NoError(t, err)

	// Put a second key with the Ephemeral flag.
	err = otherConn.Put(ctx, secondKey, secondValue, &store.WriteOptions{TTL: 2 * time.Second})
	require.NoError(t, err)

	// Get on firstKey should work.
	pair, err := kv.Get(ctx, firstKey, nil)
	require.NoError(t, err)
	checkPairNotNil(t, pair)

	// Get on secondKey should work.
	pair, err = kv.Get(ctx, secondKey, nil)
	require.NoError(t, err)
	checkPairNotNil(t, pair)

	// Close the connection.
	_ = otherConn.Close()

	// Let the session expire.
	time.Sleep(3 * time.Second)

	// Get on firstKey shouldn't work.
	pair, err = kv.Get(ctx, firstKey, nil)
	assert.Error(t, err)
	assert.Nil(t, pair)

	// Get on secondKey shouldn't work.
	pair, err = kv.Get(ctx, secondKey, nil)
	assert.Error(t, err)
	assert.Nil(t, pair)
}

func testList(t *testing.T, kv store.Store) {
	t.Helper()

	parentKey := "testList"
	childKey := "testList/child"
	subfolderKey := "testList/subfolder"

	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
	defer cancel()

	// Put the parent key.
	err := kv.Put(ctx, parentKey, nil, &store.WriteOptions{IsDir: true})
	require.NoError(t, err)

	// Put the first child key.
	err = kv.Put(ctx, childKey, []byte("first"), nil)
	require.NoError(t, err)

	// Put the second child key which is also a directory.
	err = kv.Put(ctx, subfolderKey, []byte("second"), &store.WriteOptions{IsDir: true})
	require.NoError(t, err)

	// Put child keys under secondKey.
	for i := 1; i <= 3; i++ {
		key := "testList/subfolder/key" + strconv.Itoa(i)
		err = kv.Put(ctx, key, []byte("value"), nil)
		require.NoError(t, err)
	}

	// List should work and return five child entries.
	for _, parent := range []string{parentKey, parentKey + "/"} {
		pairs, errList := kv.List(ctx, parent, nil)
		require.NoError(t, errList)
		assert.Len(t, pairs, 5)
	}

	// List on childKey should return 0 keys.
	pairs, err := kv.List(ctx, childKey, nil)
	require.NoError(t, err)
	assert.Empty(t, pairs)

	// List on subfolderKey should return 3 keys without the directory.
	pairs, err = kv.List(ctx, subfolderKey, nil)
	require.NoError(t, err)
	assert.Len(t, pairs, 3)

	// List should fail: the key does not exist.
	pairs, err = kv.List(ctx, "idontexist", nil)
	assert.ErrorIs(t, err, store.ErrKeyNotFound)
	assert.Nil(t, pairs)
}

func testListLockKey(t *testing.T, kv store.Store) {
	t.Helper()

	listKey := "testListLockSide"

	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
	defer cancel()

	err := kv.Put(ctx, listKey, []byte("val"), &store.WriteOptions{IsDir: true})
	require.NoError(t, err)

	err = kv.Put(ctx, listKey+"/subfolder", []byte("val"), &store.WriteOptions{IsDir: true})
	require.NoError(t, err)

	// Put keys under subfolder.
	for i := 1; i <= 3; i++ {
		key := listKey + "/subfolder/key" + strconv.Itoa(i)
		errPut := kv.Put(ctx, key, []byte("val"), nil)
		require.NoError(t, errPut)

		// We lock the child key.
		lock, errPut := kv.NewLock(ctx, key, &store.LockOptions{Value: []byte("locked"), TTL: 2 * time.Second})
		require.NoError(t, errPut)
		require.NotNil(t, lock)

		lockChan, errPut := lock.Lock(ctx)
		require.NoError(t, errPut)
		assert.NotNil(t, lockChan)
	}

	// List children of the root directory (`listKey`), this should
	// not output any `___lock` entries and must contain 4 results.
	pairs, err := kv.List(ctx, listKey, nil)
	require.NoError(t, err)
	assert.Len(t, pairs, 4)

	for _, pair := range pairs {
		if strings.Contains(pair.Key, "___lock") {
			assert.FailNow(t, "tesListLockKey: found a key containing lock suffix '___lock'")
		}
	}
}

func testDeleteTree(t *testing.T, kv store.Store) {
	t.Helper()

	prefix := "testDeleteTree"

	firstKey := "testDeleteTree/first"
	firstValue := []byte("first")

	secondKey := "testDeleteTree/second"
	secondValue := []byte("second")

	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
	defer cancel()

	// Put the first key.
	err := kv.Put(ctx, firstKey, firstValue, nil)
	require.NoError(t, err)

	// Put the second key.
	err = kv.Put(ctx, secondKey, secondValue, nil)
	require.NoError(t, err)

	// Get should work on the first Key.
	pair, err := kv.Get(ctx, firstKey, nil)
	require.NoError(t, err)
	checkPairNotNil(t, pair)
	assert.Equal(t, pair.Value, firstValue)
	assert.NotEqual(t, 0, pair.LastIndex)

	// Get should work on the second Key.
	pair, err = kv.Get(ctx, secondKey, nil)
	require.NoError(t, err)
	checkPairNotNil(t, pair)
	assert.Equal(t, pair.Value, secondValue)
	assert.NotEqual(t, 0, pair.LastIndex)

	// Delete Values under directory `nodes`.
	err = kv.DeleteTree(ctx, prefix)
	require.NoError(t, err)

	// Get should fail on both keys.
	pair, err = kv.Get(ctx, firstKey, nil)
	assert.Error(t, err)
	assert.Nil(t, pair)

	pair, err = kv.Get(ctx, secondKey, nil)
	assert.Error(t, err)
	assert.Nil(t, pair)
}

// RunCleanup cleans up keys introduced by the tests.
func RunCleanup(t *testing.T, kv store.Store) {
	t.Helper()

	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
	defer cancel()

	for _, key := range []string{
		"testAtomicPutWithSlashSuffixKey",
		"testPutGetDeleteExists",
		"testWatch",
		"testWatchTree",
		"testAtomicPut",
		"testAtomicPutCreate",
		"testAtomicDelete",
		"testLockUnlock",
		"testLockTTL",
		"testPutTTL",
		"testList/subfolder",
		"testList",
		"testListLockSide/subfolder",
		"testListLockSide",
		"testDeleteTree",
	} {
		err := kv.DeleteTree(ctx, key)
		if err != nil {
			assert.ErrorIsf(t, err, store.ErrKeyNotFound, "failed to delete tree key %s", key)
		}

		err = kv.Delete(ctx, key)
		if err != nil {
			assert.ErrorIsf(t, err, store.ErrKeyNotFound, "failed to delete key %s", key)
		}
	}
}


================================================
FILE: valkeyrie.go
================================================
// Package valkeyrie Distributed Key/Value Store Abstraction Library written in Go.
package valkeyrie

import (
	"context"
	"sort"
	"sync"

	"github.com/kvtools/valkeyrie/store"
)

var (
	constructorsMu sync.RWMutex
	constructors   = make(map[string]Constructor)
)

// Config the raw type of the store configurations.
type Config any

// Constructor The signature of a store constructor.
type Constructor func(ctx context.Context, endpoints []string, options Config) (store.Store, error)

// Register makes a store constructor available by the provided name.
// If Register is called twice with the same name or if constructor is nil, it panics.
func Register(name string, cttr Constructor) {
	constructorsMu.Lock()
	defer constructorsMu.Unlock()

	if cttr == nil {
		panic("valkeyrie: Register constructor is nil")
	}

	if _, dup := constructors[name]; dup {
		panic("valkeyrie: Register called twice for constructor " + name)
	}

	constructors[name] = cttr
}

// Unregister Unregisters a store.
func Unregister(storeName string) {
	constructorsMu.Lock()
	defer constructorsMu.Unlock()

	delete(constructors, storeName)
}

// UnregisterAllConstructors Unregisters all stores.
func UnregisterAllConstructors() {
	constructorsMu.Lock()
	defer constructorsMu.Unlock()

	constructors = make(map[string]Constructor)
}

// Constructors returns a sorted list of the names of the registered constructors.
func Constructors() []string {
	constructorsMu.RLock()
	defer constructorsMu.RUnlock()

	list := make([]string, 0, len(constructors))
	for name := range constructors {
		list = append(list, name)
	}

	sort.Strings(list)

	return list
}

// NewStore creates a new store instance.
func NewStore(ctx context.Context, storeName string, endpoints []string, options Config) (store.Store, error) {
	constructorsMu.RLock()
	construct, ok := constructors[storeName]
	constructorsMu.RUnlock()

	if !ok {
		return nil, &store.UnknownConstructorError{Store: storeName}
	}

	if construct == nil {
		return nil, &store.UnknownConstructorError{Store: storeName}
	}

	return construct(ctx, endpoints, options)
}


================================================
FILE: valkeyrie_test.go
================================================
package valkeyrie

import (
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestRegister(t *testing.T) {
	t.Cleanup(UnregisterAllConstructors)

	Register(testStoreName, newStore)

	assert.Len(t, constructors, 1)
}

func TestRegister_duplicate(t *testing.T) {
	t.Cleanup(UnregisterAllConstructors)

	Register(testStoreName, newStore)
	assert.Len(t, constructors, 1)

	assert.Panics(t, func() {
		Register(testStoreName, newStore)
	})
}

func TestRegister_nil(t *testing.T) {
	t.Cleanup(UnregisterAllConstructors)

	assert.Panics(t, func() {
		Register(testStoreName, nil)
	})
}

func TestUnregister(t *testing.T) {
	t.Cleanup(UnregisterAllConstructors)

	Register(testStoreName, newStore)
	assert.Len(t, constructors, 1)

	Unregister(testStoreName)

	constructorsMu.Lock()
	defer constructorsMu.Unlock()

	assert.Empty(t, constructors)
}

func TestConstructors(t *testing.T) {
	t.Cleanup(UnregisterAllConstructors)

	Register(testStoreName, newStore)
	assert.Len(t, constructors, 1)

	cttrs := Constructors()

	expected := []string{testStoreName}
	assert.Equal(t, expected, cttrs)
}

func TestNewStore(t *testing.T) {
	t.Cleanup(UnregisterAllConstructors)

	Register(testStoreName, newStore)

	assert.Len(t, constructors, 1)

	s, err := NewStore(context.Background(), testStoreName, nil, nil)
	require.NoError(t, err)

	assert.NotNil(t, s)
	assert.IsType(t, &Mock{}, s)
}
Download .txt
gitextract__0jbl6wy/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   ├── feature_request.yml
│   │   └── new_store.yml
│   ├── dependabot.yml
│   └── workflows/
│       └── build.yml
├── .gitignore
├── .golangci.yml
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── docs/
│   └── examples.md
├── go.mod
├── go.sum
├── maintainers.md
├── mock_test.go
├── readme.md
├── store/
│   ├── errors.go
│   ├── helpers.go
│   └── store.go
├── testsuite/
│   └── suite.go
├── valkeyrie.go
└── valkeyrie_test.go
Download .txt
SYMBOL INDEX (64 symbols across 7 files)

FILE: mock_test.go
  constant testStoreName (line 9) | testStoreName = "mock"
  function newStore (line 11) | func newStore(ctx context.Context, endpoints []string, options Config) (...
  type Mock (line 20) | type Mock struct
    method Put (line 31) | func (m Mock) Put(_ context.Context, _ string, _ []byte, _ *store.Writ...
    method Get (line 35) | func (m Mock) Get(_ context.Context, _ string, _ *store.ReadOptions) (...
    method Delete (line 39) | func (m Mock) Delete(_ context.Context, _ string) error {
    method Exists (line 43) | func (m Mock) Exists(_ context.Context, _ string, _ *store.ReadOptions...
    method Watch (line 47) | func (m Mock) Watch(_ context.Context, _ string, _ *store.ReadOptions)...
    method WatchTree (line 51) | func (m Mock) WatchTree(_ context.Context, _ string, _ *store.ReadOpti...
    method NewLock (line 55) | func (m Mock) NewLock(_ context.Context, _ string, _ *store.LockOption...
    method List (line 59) | func (m Mock) List(_ context.Context, _ string, _ *store.ReadOptions) ...
    method DeleteTree (line 63) | func (m Mock) DeleteTree(_ context.Context, _ string) error {
    method AtomicPut (line 67) | func (m Mock) AtomicPut(_ context.Context, _ string, _ []byte, _ *stor...
    method AtomicDelete (line 71) | func (m Mock) AtomicDelete(_ context.Context, _ string, _ *store.KVPai...
    method Close (line 75) | func (m Mock) Close() error {
  function New (line 27) | func New(_ context.Context, _ []string, cfg *Config) (*Mock, error) {

FILE: store/errors.go
  type InvalidConfigurationError (line 26) | type InvalidConfigurationError struct
    method Error (line 31) | func (e *InvalidConfigurationError) Error() string {
  type UnknownConstructorError (line 36) | type UnknownConstructorError struct
    method Error (line 40) | func (e UnknownConstructorError) Error() string {

FILE: store/helpers.go
  function CreateEndpoints (line 8) | func CreateEndpoints(addrs []string, scheme string) (entries []string) {
  function SplitKey (line 17) | func SplitKey(key string) (path []string) {

FILE: store/store.go
  type Store (line 12) | type Store interface
  type KVPair (line 54) | type KVPair struct
  type WriteOptions (line 61) | type WriteOptions struct
  type ReadOptions (line 71) | type ReadOptions struct
  type LockOptions (line 79) | type LockOptions struct
  type Locker (line 88) | type Locker interface

FILE: testsuite/suite.go
  constant testTimeout (line 16) | testTimeout = 60 * time.Second
  function RunTestCommon (line 20) | func RunTestCommon(t *testing.T, kv store.Store) {
  function RunTestListLock (line 30) | func RunTestListLock(t *testing.T, kv store.Store) {
  function RunTestAtomic (line 38) | func RunTestAtomic(t *testing.T, kv store.Store) {
  function RunTestWatch (line 49) | func RunTestWatch(t *testing.T, kv store.Store) {
  function RunTestLock (line 58) | func RunTestLock(t *testing.T, kv store.Store) {
  function RunTestLockTTL (line 66) | func RunTestLockTTL(t *testing.T, kv store.Store, backup store.Store) {
  function RunTestTTL (line 73) | func RunTestTTL(t *testing.T, kv store.Store, backup store.Store) {
  function checkPairNotNil (line 79) | func checkPairNotNil(t *testing.T, pair *store.KVPair) {
  function testPutGetDeleteExists (line 86) | func testPutGetDeleteExists(t *testing.T, kv store.Store) {
  function testWatch (line 135) | func testWatch(t *testing.T, kv store.Store) {
  function testWatchTree (line 196) | func testWatchTree(t *testing.T, kv store.Store) {
  function testAtomicPut (line 251) | func testAtomicPut(t *testing.T, kv store.Store) {
  function testAtomicPutCreate (line 288) | func testAtomicPutCreate(t *testing.T, kv store.Store) {
  function testAtomicPutWithSlashSuffixKey (line 321) | func testAtomicPutWithSlashSuffixKey(t *testing.T, kv store.Store) {
  function testAtomicDelete (line 333) | func testAtomicDelete(t *testing.T, kv store.Store) {
  function testLockUnlock (line 373) | func testLockUnlock(t *testing.T, kv store.Store) {
  function testLockTTL (line 423) | func testLockTTL(t *testing.T, kv store.Store, otherConn store.Store) {
  function testPutTTL (line 511) | func testPutTTL(t *testing.T, kv store.Store, otherConn store.Store) {
  function testList (line 558) | func testList(t *testing.T, kv store.Store) {
  function testListLockKey (line 610) | func testListLockKey(t *testing.T, kv store.Store) {
  function testDeleteTree (line 653) | func testDeleteTree(t *testing.T, kv store.Store) {
  function RunCleanup (line 704) | func RunCleanup(t *testing.T, kv store.Store) {

FILE: valkeyrie.go
  type Config (line 18) | type Config
  type Constructor (line 21) | type Constructor
  function Register (line 25) | func Register(name string, cttr Constructor) {
  function Unregister (line 41) | func Unregister(storeName string) {
  function UnregisterAllConstructors (line 49) | func UnregisterAllConstructors() {
  function Constructors (line 57) | func Constructors() []string {
  function NewStore (line 72) | func NewStore(ctx context.Context, storeName string, endpoints []string,...

FILE: valkeyrie_test.go
  function TestRegister (line 11) | func TestRegister(t *testing.T) {
  function TestRegister_duplicate (line 19) | func TestRegister_duplicate(t *testing.T) {
  function TestRegister_nil (line 30) | func TestRegister_nil(t *testing.T) {
  function TestUnregister (line 38) | func TestUnregister(t *testing.T) {
  function TestConstructors (line 52) | func TestConstructors(t *testing.T) {
  function TestNewStore (line 64) | func TestNewStore(t *testing.T) {
Condensed preview — 23 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (67K chars).
[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "chars": 1195,
    "preview": "name: 🐞 Bug Report\ndescription: Create a report to help us improve.\nbody:\n  - type: checkboxes\n    id: terms\n    attribu"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 224,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: ❓ Questions\n    url: https://github.com/kvtools/valkeyrie/discussio"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "chars": 1054,
    "preview": "name: 💡 Feature request\ndescription: \"Suggest an idea for this project.\"\nbody:\n\n  - type: textarea\n    id: problem\n    a"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/new_store.yml",
    "chars": 516,
    "preview": "name: 🧩 Add a new type of store\ndescription: \"Proposal\"\nbody:\n\n  - type: input\n    id: store-name\n    attributes:\n      "
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 510,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: gomod\n    directory: \"/\"\n    schedule:\n      interval: monthly\n    cooldown:\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "chars": 726,
    "preview": "name: Build and test\n\non: [push, pull_request]\n\nenv:\n  GOLANGCI_LINT_VERSION: v2.11\n\npermissions: {}\n\njobs:\n  build:\n   "
  },
  {
    "path": ".gitignore",
    "chars": 32,
    "preview": ".idea/\n.DS_Store\n*.iml\ndump.rdb\n"
  },
  {
    "path": ".golangci.yml",
    "chars": 2201,
    "preview": "version: \"2\"\n\nformatters:\n  enable:\n    - gci\n    - gofumpt\n  settings:\n    gofumpt:\n      extra-rules: false\n\nlinters:\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 3633,
    "preview": "# Contributing\n\nAll contributions are useful, whether it is a simple typo, a more complex change, or just pointing out a"
  },
  {
    "path": "LICENSE",
    "chars": 10804,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "Makefile",
    "chars": 152,
    "preview": ".PHONY: all\nall: validate test\n\n## Run validates\n.PHONY: validate\nvalidate:\n\tgolangci-lint run\n\n## Run tests\n.PHONY: tes"
  },
  {
    "path": "docs/examples.md",
    "chars": 4532,
    "preview": "# Examples\n\nThis document contains useful example of usage for `valkeyrie`.\nIt might not be complete but provides with g"
  },
  {
    "path": "go.mod",
    "chars": 344,
    "preview": "module github.com/kvtools/valkeyrie\n\ngo 1.22\n\nrequire github.com/stretchr/testify v1.11.1\n\nrequire (\n\tgithub.com/davecgh"
  },
  {
    "path": "go.sum",
    "chars": 1680,
    "preview": "github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.1 h1"
  },
  {
    "path": "maintainers.md",
    "chars": 337,
    "preview": "- [Alexandre Beslic](https://github.com/abronan)\n- [Victor Castell](https://github.com/victorcoder)\n- [Nicolas Mengin](h"
  },
  {
    "path": "mock_test.go",
    "chars": 1941,
    "preview": "package valkeyrie\n\nimport (\n\t\"context\"\n\n\t\"github.com/kvtools/valkeyrie/store\"\n)\n\nconst testStoreName = \"mock\"\n\nfunc newS"
  },
  {
    "path": "readme.md",
    "chars": 4664,
    "preview": "<p align=\"center\">\n  <img alt=\"golangci-lint logo\" src=\"docs/valkeyrie.png\" height=\"350\" />\n  <h3 align=\"center\">Valkeyr"
  },
  {
    "path": "store/errors.go",
    "chars": 1809,
    "preview": "package store\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nvar (\n\t// ErrCallNotSupported is thrown when a method is not implemented/sup"
  },
  {
    "path": "store/helpers.go",
    "chars": 476,
    "preview": "package store\n\nimport (\n\t\"strings\"\n)\n\n// CreateEndpoints creates a list of endpoints given the right scheme.\nfunc Create"
  },
  {
    "path": "store/store.go",
    "chars": 3266,
    "preview": "// Package store contains KV store backends.\npackage store\n\nimport (\n\t\"context\"\n\t\"time\"\n)\n\n// Store represents the backe"
  },
  {
    "path": "testsuite/suite.go",
    "chars": 18285,
    "preview": "// Package testsuite the valkeyrie tests suite.\npackage testsuite\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n"
  },
  {
    "path": "valkeyrie.go",
    "chars": 2094,
    "preview": "// Package valkeyrie Distributed Key/Value Store Abstraction Library written in Go.\npackage valkeyrie\n\nimport (\n\t\"contex"
  },
  {
    "path": "valkeyrie_test.go",
    "chars": 1431,
    "preview": "package valkeyrie\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/r"
  }
]

About this extraction

This page contains the full source code of the abronan/valkeyrie GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 23 files (60.5 KB), approximately 16.8k tokens, and a symbol index with 64 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!