2015-07-26 16:33:29 +02:00
|
|
|
// Copyright 2014, 2015 by Sascha L. Teichmann
|
2014-08-03 15:59:56 +02:00
|
|
|
// Use of this source code is governed by the MIT license
|
|
|
|
// that can be found in the LICENSE file.
|
|
|
|
|
2024-01-07 11:12:42 +01:00
|
|
|
// Package main implements a fake Redis server which can be configured
|
|
|
|
// as Redis Backend in Minetest server. It tracks the changes in the
|
|
|
|
// database and forwards them to the mtwebmapper.
|
2014-08-03 00:01:14 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"net"
|
|
|
|
"os"
|
|
|
|
"os/signal"
|
2014-08-06 10:52:37 +02:00
|
|
|
"runtime"
|
2017-03-05 14:03:02 +01:00
|
|
|
"strings"
|
2024-01-06 10:34:37 +01:00
|
|
|
"syscall"
|
2014-08-06 10:52:37 +02:00
|
|
|
"time"
|
2015-07-26 22:04:16 +02:00
|
|
|
|
|
|
|
"bitbucket.org/s_l_teichmann/mtsatellite/common"
|
2014-08-03 00:01:14 +02:00
|
|
|
)
|
|
|
|
|
2014-08-06 10:52:37 +02:00
|
|
|
const (
|
2015-03-14 19:01:00 +01:00
|
|
|
defaultMaxBulkStringSize = 32 * 1024 * 1024
|
2016-04-24 19:41:15 +02:00
|
|
|
defaultGCDuration = "24h"
|
|
|
|
defaultChangeDuration = "30s"
|
2014-08-06 10:52:37 +02:00
|
|
|
)
|
2014-08-06 10:04:24 +02:00
|
|
|
|
2014-09-07 15:02:39 +02:00
|
|
|
func usage() {
|
|
|
|
fmt.Fprintf(os.Stderr,
|
|
|
|
"Usage: %s [<options>] <database>\n", os.Args[0])
|
|
|
|
fmt.Fprintln(os.Stderr, "Options:")
|
|
|
|
flag.PrintDefaults()
|
|
|
|
}
|
|
|
|
|
2014-08-03 00:01:14 +02:00
|
|
|
func main() {
|
|
|
|
|
2014-08-06 10:52:37 +02:00
|
|
|
var (
|
2015-03-14 19:01:00 +01:00
|
|
|
port int
|
|
|
|
host string
|
|
|
|
driver string
|
|
|
|
cacheSize int
|
|
|
|
version bool
|
|
|
|
interleaved bool
|
2015-05-27 16:57:08 +02:00
|
|
|
changeURL string
|
2015-03-14 19:01:00 +01:00
|
|
|
gcDuration string
|
|
|
|
changeDuration string
|
|
|
|
maxBulkStringSize int64
|
2014-08-06 10:52:37 +02:00
|
|
|
)
|
2014-08-03 00:01:14 +02:00
|
|
|
|
2014-09-07 15:02:39 +02:00
|
|
|
flag.Usage = usage
|
|
|
|
|
2014-08-03 00:01:14 +02:00
|
|
|
flag.IntVar(&port, "port", 6379, "port to bind")
|
2014-08-03 14:52:24 +02:00
|
|
|
flag.StringVar(&driver, "driver", "leveldb", "type of database (leveldb, sqlite)")
|
2014-08-03 00:01:14 +02:00
|
|
|
flag.StringVar(&host, "host", "", "host to bind")
|
|
|
|
flag.IntVar(&cacheSize, "cache", 32, "cache size in MB")
|
2014-08-06 10:04:24 +02:00
|
|
|
flag.BoolVar(&version, "version", false, "Print version and exit.")
|
2014-09-17 09:51:34 +02:00
|
|
|
flag.BoolVar(&interleaved,
|
|
|
|
"interleaved", false, "Backend stores key in interleaved form.")
|
|
|
|
flag.StringVar(&gcDuration,
|
2016-04-24 19:41:15 +02:00
|
|
|
"gc-duration", defaultGCDuration, "Duration between forced GCs.")
|
2014-09-17 09:51:34 +02:00
|
|
|
flag.StringVar(&changeDuration,
|
2016-04-24 19:41:15 +02:00
|
|
|
"change-duration", defaultChangeDuration, "Duration to aggregate changes.")
|
2015-05-27 16:57:08 +02:00
|
|
|
flag.StringVar(&changeURL, "change-url", "", "URL to send changes to.")
|
2015-03-14 19:01:00 +01:00
|
|
|
flag.Int64Var(&maxBulkStringSize, "max-bulk-string-size", defaultMaxBulkStringSize,
|
|
|
|
"max size of a bulk string to be accepted as input (in bytes).")
|
2014-08-03 00:01:14 +02:00
|
|
|
flag.Parse()
|
|
|
|
|
2014-08-06 10:04:24 +02:00
|
|
|
if version {
|
2015-07-26 22:04:16 +02:00
|
|
|
common.PrintVersionAndExit()
|
2014-08-06 10:04:24 +02:00
|
|
|
}
|
|
|
|
|
2014-08-23 19:00:43 +02:00
|
|
|
if flag.NArg() < 1 {
|
2014-08-03 00:01:14 +02:00
|
|
|
log.Fatal("Missing path to world")
|
|
|
|
}
|
|
|
|
|
2014-08-06 10:52:37 +02:00
|
|
|
var (
|
2014-09-17 09:51:34 +02:00
|
|
|
err error
|
2024-01-07 10:54:21 +01:00
|
|
|
backend backend
|
2014-09-17 09:51:34 +02:00
|
|
|
gcDur time.Duration
|
|
|
|
chDur time.Duration
|
2016-04-24 19:41:15 +02:00
|
|
|
changeTracker *changeTracker
|
2014-08-06 10:52:37 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
if gcDur, err = time.ParseDuration(gcDuration); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2014-08-03 00:01:14 +02:00
|
|
|
|
2014-09-17 09:51:34 +02:00
|
|
|
// Setup the change listening stuff.
|
|
|
|
|
|
|
|
var changeChan <-chan time.Time
|
|
|
|
|
2015-05-27 16:57:08 +02:00
|
|
|
useChangeNotification := changeURL != ""
|
2014-09-17 09:51:34 +02:00
|
|
|
|
|
|
|
if useChangeNotification {
|
|
|
|
if chDur, err = time.ParseDuration(changeDuration); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
changeChan = time.Tick(chDur)
|
2016-04-24 19:41:15 +02:00
|
|
|
changeTracker = newChangeTracker()
|
2014-09-17 09:51:34 +02:00
|
|
|
} else {
|
|
|
|
// We will never receive ticks on this.
|
|
|
|
changeChan = make(<-chan time.Time)
|
|
|
|
}
|
|
|
|
|
2014-08-23 19:00:43 +02:00
|
|
|
path := flag.Arg(0)
|
|
|
|
|
2014-08-03 15:42:12 +02:00
|
|
|
if driver == "sqlite" {
|
2024-01-07 11:04:29 +01:00
|
|
|
if backend, err = newSQLiteBackend(path, changeTracker, interleaved); err != nil {
|
2014-08-03 15:42:12 +02:00
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
} else {
|
2024-01-07 11:04:29 +01:00
|
|
|
if backend, err = newLeveDBBackend(
|
2014-09-17 09:51:34 +02:00
|
|
|
path, changeTracker, interleaved, cacheSize); err != nil {
|
2014-08-03 15:42:12 +02:00
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
2014-09-17 09:51:34 +02:00
|
|
|
|
2024-01-07 10:54:21 +01:00
|
|
|
defer backend.shutdown()
|
2014-08-03 00:01:14 +02:00
|
|
|
|
|
|
|
var listener net.Listener
|
|
|
|
|
2017-03-05 14:03:02 +01:00
|
|
|
var proto, address string
|
|
|
|
if strings.ContainsRune(host, '/') {
|
|
|
|
proto, address = "unix", host
|
|
|
|
} else {
|
|
|
|
proto, address = "tcp", fmt.Sprintf("%s:%d", host, port)
|
|
|
|
}
|
|
|
|
|
|
|
|
listener, err = net.Listen(proto, address)
|
2014-08-03 00:01:14 +02:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
defer listener.Close()
|
2015-07-20 14:19:41 +02:00
|
|
|
log.Printf("Server started at %s\n", listener.Addr())
|
2014-08-03 00:01:14 +02:00
|
|
|
|
|
|
|
connChan := make(chan net.Conn)
|
|
|
|
defer close(connChan)
|
|
|
|
sigChan := make(chan os.Signal, 1)
|
2024-01-06 10:34:37 +01:00
|
|
|
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
2014-08-03 00:01:14 +02:00
|
|
|
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
conn, err := listener.Accept()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2015-07-20 14:19:41 +02:00
|
|
|
log.Printf("Client accepted from: %s\n", conn.RemoteAddr())
|
2014-08-03 00:01:14 +02:00
|
|
|
connChan <- conn
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2015-07-20 14:19:41 +02:00
|
|
|
log.Printf("Doing garbage collection every: %s\n", gcDur)
|
2014-08-06 10:52:37 +02:00
|
|
|
gcChan := time.Tick(gcDur)
|
|
|
|
|
2014-08-03 00:01:14 +02:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case conn := <-connChan:
|
2024-01-07 10:54:21 +01:00
|
|
|
var session session
|
|
|
|
if session, err = backend.newSession(); err != nil {
|
2015-07-20 14:19:41 +02:00
|
|
|
log.Printf("Cannot create session: %s\n", err)
|
2014-08-06 01:11:41 +02:00
|
|
|
conn.Close()
|
|
|
|
} else {
|
2024-01-07 04:22:22 +01:00
|
|
|
go newConnection(conn, session, maxBulkStringSize).run()
|
2014-08-06 01:11:41 +02:00
|
|
|
}
|
2014-08-03 00:01:14 +02:00
|
|
|
case <-sigChan:
|
|
|
|
log.Println("Shutting down")
|
|
|
|
return
|
2014-08-06 10:52:37 +02:00
|
|
|
case <-gcChan:
|
|
|
|
log.Println("Starting garbage collection.")
|
|
|
|
runtime.GC()
|
|
|
|
log.Println("Garbage collection done.")
|
2014-09-17 09:51:34 +02:00
|
|
|
case <-changeChan:
|
|
|
|
if changeTracker != nil {
|
2015-05-27 16:57:08 +02:00
|
|
|
changeTracker.FlushChanges(changeURL)
|
2014-09-17 09:51:34 +02:00
|
|
|
}
|
2014-08-03 00:01:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|