Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
dfb658a
WIP: config rewrite in progress
SuperALKALINEdroiD Apr 27, 2025
f3400ae
added app path
SuperALKALINEdroiD Apr 27, 2025
ea699c7
load config using environment variable: LOG_BASE_SETTINGS
SuperALKALINEdroiD Apr 27, 2025
b79031b
fixed the destination address for client connection
SuperALKALINEdroiD Apr 27, 2025
6f01088
WIP storage manifest for in memory storage
SuperALKALINEdroiD May 11, 2025
4012a54
WIP storage manifest for in memory storage
SuperALKALINEdroiD May 11, 2025
5ee6776
WIP: memory storage
SuperALKALINEdroiD May 18, 2025
c7796c5
WIP: memort storage and planning some arch changes
SuperALKALINEdroiD May 18, 2025
8e1fb60
refactor get value endpoint
SuperALKALINEdroiD Jul 6, 2025
bb2c96c
WIP bloom filters: Added Bloom Filters
SuperALKALINEdroiD Jul 26, 2025
325c70c
WIP: atomic flush to storage
SuperALKALINEdroiD Jul 26, 2025
4a8d0cf
remove log prefix, setting up a potential logger for future
SuperALKALINEdroiD Jul 26, 2025
60a895c
WIP: tweaks around persistance
SuperALKALINEdroiD Jan 17, 2026
1f7b5c9
WIP: replay and persistance
SuperALKALINEdroiD Jan 18, 2026
f6c45d4
persist data on disk
SuperALKALINEdroiD Feb 15, 2026
da87c14
grpc pool to avoid new connection per call
SuperALKALINEdroiD Apr 12, 2026
0b85fa3
Apply suggestions from code review
SuperALKALINEdroiD May 17, 2026
c8ce935
WIP: persistance, replay and connections
SuperALKALINEdroiD Jul 4, 2026
04e3576
refactored locking
SuperALKALINEdroiD Aug 2, 2026
1b3010d
remove extra line
SuperALKALINEdroiD Sep 5, 2026
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ This is a distributed, in-memory key-value store with write-ahead logging (WAL)
- [ ] **Persistent Disk Storage**: Working on storing data to disk efficiently.
- [ ] **Additional Endpoints**: Implementing `GET`, `UPDATE`, `DELETE`, and other operations.
- [ ] **GraphQL & Live Queries**: Exploring GraphQL or similar solutions for reactivity.
- [ ] Implement red black tree by myself

## Future Plans
- **Replication & Failover**: Implement strategies for high availability.
Expand Down
Empty file added cmd/cpu_profile.prof
Empty file.
2 changes: 1 addition & 1 deletion cmd/default-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@
"inMemoryStorageThreshold": 2000,
"metaDataConfig": {
"state": 1,
"walPath": "/var/lib/db/wal"
"walPath": "../runtime-files/wal-storage"
}
}
108 changes: 78 additions & 30 deletions cmd/init.go
Original file line number Diff line number Diff line change
@@ -1,62 +1,110 @@
package main

import (
"context"
"fmt"
"log"
"net/http"
"os"
"slices"
"time"

"github.com/SuperALKALINEdroiD/timelyDB/config"
"github.com/SuperALKALINEdroiD/timelyDB/core"
"github.com/SuperALKALINEdroiD/timelyDB/handlers"
"github.com/go-chi/chi/middleware"
"github.com/go-chi/chi/v5"
"github.com/SuperALKALINEdroiD/timelyDB/utils/common"
"github.com/google/uuid"
)

func initEnvironment() (*config.DatabaseConfig, error) {
var configPath = os.Getenv("CONFIG_PATH")

fmt.Println(configPath)
var configPath = os.Getenv("LOG_BASE_SETTINGS")

cfg, err := config.LoadConfig(configPath)
if err != nil {
log.Printf("Error loading configuration: %v", err)
return nil, err
}

return cfg, nil
}

func initRouter(app *core.App) *chi.Mux {
router := chi.NewRouter()
addMiddlewares(router)
initRoutes(router, app)
return router
func GetAppPath() string {
return common.GetAppPath()
}

func initRouter(app *core.App) *http.ServeMux {
mux := http.NewServeMux()
initRoutes(mux, app)
return mux
}

func addMiddlewares(router *chi.Mux) {
router.Use(middleware.RealIP)
router.Use(middleware.RequestID)
router.Use(middleware.Logger)
func initRoutes(mux *http.ServeMux, app *core.App) {
mux.HandleFunc("GET /data-in/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "Server is running")
})

mux.HandleFunc("POST /data-in/upsert", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Upsert Endpoint WIP - Config: %+v", app)
})

mux.HandleFunc("POST /data-in/insert", handlers.InsertHandler(app))

mux.HandleFunc("GET /data-in/", handlers.GetValue(app))

mux.HandleFunc("POST /data-in/update", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Update Endpoint WIP - Config: %+v", app)
})
}

func initRoutes(router *chi.Mux, app *core.App) {
// init routes based on config ??
router.Route("/data-in", func(r chi.Router) {
r.Get("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "Server is running")
})
func middleware(h http.Handler, m ...func(http.Handler) http.Handler) http.Handler {
for _, value := range slices.Backward(m) {
h = value(h)
}

r.Post("/upsert", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Upsert Endpoint WIP - Config: %+v", app)
})
return h
}

r.Post("/insert", handlers.InsertHandler(app))
func realIP(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if ip := r.Header.Get("X-Real-IP"); ip != "" {
r.RemoteAddr = ip
} else if ip := r.Header.Get("X-Forwarded-For"); ip != "" {
r.RemoteAddr = ip
}
next.ServeHTTP(w, r)
})
}

r.Post("/update", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Update Endpoint WIP - Config: %+v", app)
})
func requestID(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
id := r.Header.Get("X-Request-ID")
if id == "" {
id = uuid.NewString()
}
w.Header().Set("X-Request-ID", id)
ctx := context.WithValue(r.Context(), "requestID", id)
next.ServeHTTP(w, r.WithContext(ctx))
})
}

func requestLogger(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
rw := &statusResponseWriter{ResponseWriter: w, status: http.StatusOK}
next.ServeHTTP(rw, r)
log.Printf("%s %s %d %s", r.Method, r.URL.Path, rw.status, time.Since(start))
})
}

type statusResponseWriter struct {
http.ResponseWriter
status int
}

func (rw *statusResponseWriter) WriteHeader(code int) {
rw.status = code
rw.ResponseWriter.WriteHeader(code)
}
57 changes: 48 additions & 9 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ import (
"net/http"
"os"
"os/signal"
"runtime/pprof"
"path/filepath"
"syscall"
"time"

"github.com/SuperALKALINEdroiD/timelyDB/core"
"github.com/SuperALKALINEdroiD/timelyDB/utils/common"
"github.com/SuperALKALINEdroiD/timelyDB/utils/logs"
"github.com/SuperALKALINEdroiD/timelyDB/utils/nodes"
"github.com/SuperALKALINEdroiD/timelyDB/utils/storage"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)

func main() {
Expand All @@ -24,10 +27,6 @@ func main() {
}
defer f.Close()

if err := pprof.StartCPUProfile(f); err != nil {
panic(err)
}
defer pprof.StopCPUProfile()
ctx, cancel := context.WithCancel(context.Background())

signalChannel := make(chan os.Signal, 1)
Expand All @@ -45,24 +44,54 @@ func main() {
panic("error while loading config")
}

grpcNodes, nodeHashInfo := nodes.LoadServers(ctx, config)
wal := &storage.LocalWAL{}
wal.Connect("wal-storage")
appPath := common.GetAppPath()
wal.Connect(filepath.Join(appPath, config.MetaDataConfig.WALName))

grpcNodes, nodeHashInfo := nodes.LoadServers(ctx, config, wal)

storageNodesIndex := make(map[string]*nodes.Node, len(grpcNodes))
nodeClients := make(map[string]nodes.NodeServiceClient, len(grpcNodes))
nodeConns := make(map[string]*grpc.ClientConn, len(grpcNodes))

for _, n := range grpcNodes {
if n == nil {
continue
}
storageNodesIndex[n.ID] = n
conn, connErr := grpc.NewClient(n.Address, grpc.WithTransportCredentials(insecure.NewCredentials()))
if connErr != nil {
log.Fatalf("failed to create gRPC client for node %s: %v", n.ID, connErr)
}
nodeClients[n.ID] = nodes.NewNodeServiceClient(conn)
nodeConns[n.ID] = conn
}

app := &core.App{
Config: config,
Nodes: grpcNodes,
NodeByID: storageNodesIndex,
NodeClients: nodeClients,
NodeConns: nodeConns,
NodeHashInfo: nodeHashInfo,
WAL: wal,
}

logs.ReplayLogs(app)

router := initRouter(app)
handler := middleware(router, realIP, requestID, requestLogger)

serverAddress := fmt.Sprintf(":%d", app.Config.Port)
log.Printf("Starting server on %s", serverAddress)
server := &http.Server{Addr: ":7001", Handler: router}
log.Printf("Starting %s server on %s", config.StoreName, serverAddress)
server := &http.Server{
Addr: serverAddress,
Handler: handler,
ReadHeaderTimeout: 5 * time.Second,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
}

go func() {
log.Printf("Starting to listen on %s", serverAddress)
Expand All @@ -74,13 +103,23 @@ func main() {
<-ctx.Done()
log.Println("Shutting down main server...")

if err := app.WAL.Flush(); err != nil {
log.Printf("WAL flush on shutdown failed: %v", err)
}

shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
defer shutdownCancel()

if err := server.Shutdown(shutdownCtx); err != nil {
log.Fatalf("Server shutdown failed: %v", err)
}

for nodeID, conn := range app.NodeConns {
if err := conn.Close(); err != nil {
log.Printf("failed to close gRPC client for node %s: %v", nodeID, err)
}
}

log.Println("Exiting, Bye!")

}
10 changes: 0 additions & 10 deletions cmd/wal-storage

This file was deleted.

Loading