HTRACE-309. htraced: improve leveldb configuration (Colin Patrick McCabe via iwasakims)
diff --git a/htrace-htraced/go/src/org/apache/htrace/conf/config_keys.go b/htrace-htraced/go/src/org/apache/htrace/conf/config_keys.go
index 10204cd..6f6e8cf 100644
--- a/htrace-htraced/go/src/org/apache/htrace/conf/config_keys.go
+++ b/htrace-htraced/go/src/org/apache/htrace/conf/config_keys.go
@@ -96,6 +96,10 @@
// this to read or write a message, we will abort the connection.
const HTRACE_HRPC_IO_TIMEOUT_MS = "hrpc.io.timeout.ms"
+// The leveldb write buffer size, or 0 to use the library default, which is 4
+// MB in leveldb 1.16. See leveldb's options.h for more details.
+const HTRACE_LEVELDB_WRITE_BUFFER_SIZE = "leveldb.write.buffer.size"
+
// Default values for HTrace configuration keys.
var DEFAULTS = map[string]string{
HTRACE_WEB_ADDRESS: fmt.Sprintf("0.0.0.0:%d", HTRACE_WEB_ADDRESS_DEFAULT_PORT),
@@ -112,6 +116,7 @@
HTRACE_REAPER_HEARTBEAT_PERIOD_MS: fmt.Sprintf("%d", 90*1000),
HTRACE_NUM_HRPC_HANDLERS: "20",
HTRACE_HRPC_IO_TIMEOUT_MS: "60000",
+ HTRACE_LEVELDB_WRITE_BUFFER_SIZE: "0",
}
// Values to be used when creating test configurations
diff --git a/htrace-htraced/go/src/org/apache/htrace/htraced/datastore.go b/htrace-htraced/go/src/org/apache/htrace/htraced/datastore.go
index 2a3d65c..828a6af 100644
--- a/htrace-htraced/go/src/org/apache/htrace/htraced/datastore.go
+++ b/htrace-htraced/go/src/org/apache/htrace/htraced/datastore.go
@@ -27,6 +27,7 @@
"fmt"
"github.com/jmhodges/levigo"
"github.com/ugorji/go/codec"
+ "math"
"org/apache/htrace/common"
"org/apache/htrace/conf"
"os"
@@ -34,6 +35,7 @@
"strings"
"sync"
"sync/atomic"
+ "syscall"
"time"
)
@@ -460,6 +462,9 @@
// When this datastore was started (in UTC milliseconds since the epoch)
startMs int64
+
+ // The maximum number of open files to allow per shard.
+ maxFdPerShard int
}
func CreateDataStore(cnf *conf.Config, writtenSpans *common.Semaphore) (*dataStore, error) {
@@ -482,6 +487,7 @@
store.readOpts = levigo.NewReadOptions()
store.readOpts.SetFillCache(true)
+ store.readOpts.SetVerifyChecksums(false)
store.writeOpts = levigo.NewWriteOptions()
store.writeOpts.SetSync(false)
@@ -514,9 +520,49 @@
})
}
store.startMs = common.TimeToUnixMs(time.Now().UTC())
+ err = store.calculateMaxOpenFilesPerShard()
+ if err != nil {
+ lg.Warnf("Unable to calculate maximum open files per shard: %s\n",
+ err.Error())
+ }
return store, nil
}
+// The maximum number of file descriptors we'll use on non-datastore things.
+const NON_DATASTORE_FD_MAX = 300
+
+// The minimum number of file descriptors per shard we will set. Setting fewer
+// than this number could trigger a bug in some early versions of leveldb.
+const MIN_FDS_PER_SHARD = 80
+
+func (store *dataStore) calculateMaxOpenFilesPerShard() error {
+ var rlim syscall.Rlimit
+ err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlim)
+ if err != nil {
+ return err
+ }
+ // I think RLIMIT_NOFILE fits in 32 bits on all known operating systems,
+ // but there's no harm in being careful. 'int' in golang always holds at
+ // least 32 bits.
+ var maxFd int
+ if rlim.Cur > uint64(math.MaxInt32) {
+ maxFd = math.MaxInt32
+ } else {
+ maxFd = int(rlim.Cur)
+ }
+ fdsPerShard := (maxFd - NON_DATASTORE_FD_MAX) / len(store.shards)
+ if fdsPerShard < MIN_FDS_PER_SHARD {
+ return errors.New(fmt.Sprintf("Expected to be able to use at least %d " +
+ "fds per shard, but we have %d shards and %d total fds to allocate, " +
+ "giving us only %d FDs per shard.", MIN_FDS_PER_SHARD,
+ len(store.shards), maxFd - NON_DATASTORE_FD_MAX, fdsPerShard))
+ }
+ store.lg.Infof("maxFd = %d. Setting maxFdPerShard = %d\n",
+ maxFd, fdsPerShard)
+ store.maxFdPerShard = fdsPerShard
+ return nil
+}
+
func CreateShard(store *dataStore, cnf *conf.Config, path string,
clearStored bool) (*shard, error) {
lg := store.lg
@@ -543,6 +589,14 @@
}
var shd *shard
openOpts := levigo.NewOptions()
+ openOpts.SetParanoidChecks(false)
+ writeBufferSize := cnf.GetInt(conf.HTRACE_LEVELDB_WRITE_BUFFER_SIZE)
+ if writeBufferSize > 0 {
+ openOpts.SetWriteBufferSize(writeBufferSize)
+ }
+ if store.maxFdPerShard > 0 {
+ openOpts.SetMaxOpenFiles(store.maxFdPerShard)
+ }
defer openOpts.Close()
newlyCreated := false
ldb, err := levigo.Open(path, openOpts)
@@ -636,8 +690,10 @@
store.hb = nil
}
for idx := range store.shards {
- store.shards[idx].Close()
- store.shards[idx] = nil
+ if store.shards[idx] != nil {
+ store.shards[idx].Close()
+ store.shards[idx] = nil
+ }
}
if store.rpr != nil {
store.rpr.Shutdown()