linxGnu / grocksdb

RocksDB wrapper for Go. Support 9.x, 8.x, 7.x, 6.x, etc

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Custom Comparator in Go

muthukrishnan24 opened this issue · comments

Comparator interface needs Native method

Native() *C.rocksdb_comparator_t

Cgo translates C types into equivalent unexported Go types. Because the translations are unexported, a Go package should not expose C types in its exported API: a C type used in one Go package is different from the same C type used in another.

Because of this, it is not possible to implement this interface.

@muthukrishnan24 please forgive me that I did not fully understand your question?

Could you please describe it in simpler way?

  • What you want to archive? New API? Expose native pointer?
  • What is your motivation to do so

I'm trying to create a Custom Comparator for a CF

package main

// #include "rocksdb/c.h"
import "C"
import (
	"bytes"
	"fmt"
	"os"

	"github.com/linxGnu/grocksdb"
)

func main() {
	opts := grocksdb.NewDefaultOptions()
	opts.SetCreateIfMissing(true)

	defOpts := grocksdb.NewDefaultOptions()
	defOpts.SetComparator(&CustomComparator{})

	db, cfHands, err := grocksdb.OpenDbColumnFamilies(opts, "testdb", []string{"default"}, []*grocksdb.Options{defOpts})
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	defer db.Close()

	defCF := cfHands[0]

	writeOpts := grocksdb.NewDefaultWriteOptions()
	db.PutCF(writeOpts, defCF, []byte("Hello"), []byte("World"))
	writeOpts.Destroy()

	readOpts := grocksdb.NewDefaultReadOptions()
	defer readOpts.Destroy()

	data, err := db.GetCF(readOpts, defCF, []byte("Hello"))
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	if data.Exists() {
		fmt.Println(string(data.Data()))
	}
}

type CustomComparator struct{}

func (f *CustomComparator) Compare(a, b []byte) int {
	return bytes.Compare(b, a)
}

// The name of the comparator.
func (f *CustomComparator) Name() string {
	return "custom"
}

// Return native comparator.
func (f *CustomComparator) Native() *C.rocksdb_comparator_t {
	return nil
}

// Destroy comparator.
func (f *CustomComparator) Destroy() {}

// also not valid
// func (f *CustomComparator) Native() *grocksdb._Ctype_struct_rocksdb_comparator_t { return nil }

Since Comparator needs to implement method Native() *C.rocksdb_comparator_t

Above code throws following errors

./main.go:18:24: cannot use &CustomComparator{} (type *CustomComparator) as type grocksdb.Comparator in argument to defOpts.SetComparator:
        *CustomComparator does not implement grocksdb.Comparator (wrong type for Native method)
                have Native() *_Ctype_struct_rocksdb_comparator_t
                want Native() *grocksdb._Ctype_struct_rocksdb_comparator_t

Directly referencing the type, also not possible since it is always unexported

./main.go:59:38: cannot refer to unexported name grocksdb._Ctype_struct_rocksdb_comparator_t

@muthukrishnan24 I will reproduce at my side and tell you.

main.go file:

package main

// #cgo LDFLAGS: -lrocksdb -pthread -lstdc++ -ldl -lm -lzstd -llz4 -lz -lsnappy
// #include "rocksdb/c.h"
// #include "a.h"
import "C"

import (
	"fmt"
	"os"
	"unsafe"

	"github.com/linxGnu/grocksdb"
)

func main() {
	opts := grocksdb.NewDefaultOptions()
	opts.SetCreateIfMissing(true)

	// work:
	cmp := C.comparator_create(0)
	opts.SetNativeComparator(unsafe.Pointer(cmp))

	// Also work:
	// opts.SetComparator(grocksdb.NewComparator("abc", func(a, b []byte) int {
	// 	return bytes.Compare(b, a)
	// }))

	db, err := grocksdb.OpenDb(opts, "testdb")
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	defer db.Close()

	// insert keys
	givenKeys := [][]byte{[]byte("key1"), []byte("key2"), []byte("key3")}
	wo := grocksdb.NewDefaultWriteOptions()
	for _, k := range givenKeys {
		if err := db.Put(wo, k, []byte("val")); err != nil {
			panic(err)
		}
	}

	// create a iterator to collect the keys
	ro := grocksdb.NewDefaultReadOptions()
	iter := db.NewIterator(ro)
	defer iter.Close()

	// we seek to the last key and iterate in reverse order
	// to match given keys
	for iter.SeekToFirst(); iter.Valid(); iter.Next() {
		key := make([]byte, 4)
		copy(key, iter.Key().Data())
		fmt.Println(string(key))
	}
}

a.h file:

#include <stdlib.h>
#include "rocksdb/c.h"

// This API provides convenient C wrapper functions for rocksdb client.

/* Base */
static void gorocksdb_destruct_handler(void* state) {
}

static const char* cmp_name(void*) { 
	return "abc";
}

static int compare_custom(void* state, const char* a, size_t alen, const char* b, size_t blen) {
	for (size_t i = 0; i < alen; ++i)
	    if (a[i] > b[i]) return -1;
	return 1;
}

/* Comparator */
rocksdb_comparator_t* comparator_create(uintptr_t idx) {
	return rocksdb_comparator_create(
        (void*)idx,
        gorocksdb_destruct_handler,
        compare_custom,
        cmp_name);
}

@muthukrishnan24 I have updated the API to make your code built-able, as well as easier to create custom comparator:

  • Using go code logic as comparator
  • Using c code logic as comparator (I highly recommend this way)

The patch is included in the release: https://github.com/linxGnu/grocksdb/releases/tag/v1.6.47
PR for this patch: #67

I also gave an example above to test. PTAL