Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/ci-badger-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,37 @@ permissions:
contents: read

jobs:
cross-compile:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- goos: linux
goarch: amd64
- goos: linux
goarch: arm64
- goos: darwin
goarch: amd64
- goos: darwin
goarch: arm64
- goos: windows
goarch: amd64
- goos: aix
goarch: ppc64
- goos: plan9
goarch: amd64
steps:
- uses: actions/checkout@v5
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
- name: Cross-compile for ${{ matrix.goos }}/${{ matrix.goarch }}
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
run: go build ./...

badger-tests:
runs-on: ubuntu-latest
steps:
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,23 @@ Below is a list of known projects that use Badger:

If you are using Badger in a project please send a pull request to add it to the list.

### Platform Compatibility

Badger uses OS-specific implementations for directory locking and `fsync` operations. On
**POSIX-compliant systems** (Linux, macOS, BSD), these work as expected.

For **non-POSIX platforms**, be aware of potential limitations:

| Platform | File | Notes |
| -------- | ---------------- | -------------------------------------------------------------------- |
| AIX | `dir_aix.go` | Directory `fsync` not supported; durability on crash may be affected |
| Windows | `dir_windows.go` | Uses different locking mechanism |
| Plan9 | `dir_plan9.go` | No file locking support |
| WASM/JS | `dir_other.go` | No file locking support |

If you encounter issues on these platforms, review the corresponding `dir_*.go` source file for
implementation details.

## Contributing

If you're interested in contributing to Badger see [CONTRIBUTING](./CONTRIBUTING.md).
Expand Down
144 changes: 144 additions & 0 deletions dir_aix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
//go:build aix
Comment thread
matthewmcneely marked this conversation as resolved.
// +build aix

/*
Comment thread
pmur marked this conversation as resolved.
* SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc.
* SPDX-License-Identifier: Apache-2.0
*/

package badger

import (
"errors"
"fmt"
"os"
"path/filepath"
"sync"

"golang.org/x/sys/unix"

"github.com/dgraph-io/badger/v4/y"
)

// AIX flock locks files, not descriptors. So, multiple descriptors cannot
// be used in the same file. The first to close removals the lock on the
// file.
type directoryLockGuard struct {
// The absolute path to our pid file.
path string
// Was this a shared lock for a read-only database?
readOnly bool
}

// AIX flocking is file x process, not fd x file x process like linux. We can
// only hold one descriptor with a lock open at any given time.
type aixFlock struct {
file *os.File
count int
readOnly bool
}

// Keep a map of locks synchronized by a mutex.
var aixFlockMap = map[string]*aixFlock{}
var aixFlockMapLock sync.Mutex
Comment thread
matthewmcneely marked this conversation as resolved.

// acquireDirectoryLock gets a lock on the directory (using flock). If
// this is not read-only, it will also write our pid to
// dirPath/pidFileName for convenience.
func acquireDirectoryLock(dirPath string, pidFileName string, readOnly bool) (
*directoryLockGuard, error) {

// Convert to absolute path so that Release still works even if we do an unbalanced
// chdir in the meantime.
absPidFilePath, err := filepath.Abs(filepath.Join(dirPath, pidFileName))
if err != nil {
return nil, y.Wrapf(err, "cannot get absolute path for pid lock file")
}

aixFlockMapLock.Lock()
defer aixFlockMapLock.Unlock()

lg := &directoryLockGuard{absPidFilePath, readOnly}

if lock, fnd := aixFlockMap[absPidFilePath]; fnd {
if !readOnly || lock.readOnly != readOnly {
return nil, fmt.Errorf(
"Cannot acquire directory lock on %q. Another process is using this Badger database.", dirPath)
}
lock.count++
} else {
// This is the first acquirer, set up a lock file and register it.
f, err := os.OpenFile(absPidFilePath, os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
return nil, y.Wrapf(err, "cannot create/open pid file %q", absPidFilePath)
}

opts := unix.F_WRLCK
if readOnly {
opts = unix.F_RDLCK
}

flckt := unix.Flock_t{int16(opts), 0, 0, 0, 0, 0, 0}
err = unix.FcntlFlock(uintptr(f.Fd()), unix.F_SETLK, &flckt)
if err != nil {
f.Close()
return nil, y.Wrapf(err,
"Cannot acquire directory lock on %q. Another process is using this Badger database.", dirPath)
}

if !readOnly {
f.Truncate(0)
// Write our pid to the file.
_, err = f.Write([]byte(fmt.Sprintf("%d\n", os.Getpid())))
if err != nil {
f.Close()
return nil, y.Wrapf(err,
"Cannot write pid file %q", absPidFilePath)
}
}
aixFlockMap[absPidFilePath] = &aixFlock{f, 1, readOnly}
}
return lg, nil
}

// Release deletes the pid file and releases our lock on the directory.
func (guard *directoryLockGuard) release() error {
var err error

aixFlockMapLock.Lock()
defer aixFlockMapLock.Unlock()

if lock, fnd := aixFlockMap[guard.path]; fnd {
lock.count--
if lock.count == 0 {
if !lock.readOnly {
// Try to clear the PID if we succeed.
lock.file.Truncate(0)
os.Remove(guard.path)
}

if closeErr := lock.file.Close(); err == nil {
err = closeErr
}
delete(aixFlockMap, guard.path)
guard.path = ""
}
} else {
err = errors.New(fmt.Sprintf("unknown lock %v", guard.path))
}

return err
}

// openDir opens a directory for syncing.
func openDir(path string) (*os.File, error) { return os.Open(path) }

// When you create or delete a file, you have to ensure the directory entry for the file is synced
// in order to guarantee the file is visible (if the system crashes). (See the man page for fsync,
// or see https://github.com/coreos/etcd/issues/6368 for an example.)
func syncDir(dir string) error {
var err error
// AIX does not support fsync on a directory.
// Data durability on crash may be affected.
return err
}
4 changes: 2 additions & 2 deletions dir_unix.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//go:build !windows && !plan9 && !js && !wasip1
// +build !windows,!plan9,!js,!wasip1
//go:build !windows && !plan9 && !js && !wasip1 && !aix
// +build !windows,!plan9,!js,!wasip1,!aix

/*
* SPDX-FileCopyrightText: © 2017-2025 Istari Digital, Inc.
Expand Down