Skip to content
Open
Show file tree
Hide file tree
Changes from 93 commits
Commits
Show all changes
121 commits
Select commit Hold shift + click to select a range
5c9053b
added changes
the-tushar-meesho Dec 18, 2025
42b47fc
added changes
the-tushar-meesho Dec 18, 2025
23194aa
added changes
the-tushar-meesho Dec 18, 2025
861c2a7
addedc change
the-tushar-meesho Dec 18, 2025
11e6ccd
added
the-tushar-meesho Dec 18, 2025
0812c4e
added changes
the-tushar-meesho Dec 18, 2025
9d38a93
added changes
the-tushar-meesho Dec 18, 2025
19c9018
added changes
the-tushar-meesho Dec 18, 2025
49416e5
added large request grpc call
the-tushar-meesho Dec 19, 2025
2172d8f
remove formatting bug from rust
the-tushar-meesho Dec 19, 2025
ca30fc7
refactor unrefactor change form rust
the-tushar-meesho Dec 19, 2025
7038846
update feature schema
the-tushar-meesho Dec 19, 2025
b248827
remove worker_count from rust and reformat code
the-tushar-meesho Dec 19, 2025
55afafe
remove function name to main
the-tushar-meesho Dec 19, 2025
7a4e41f
edit some changes to make equal to go
the-tushar-meesho Dec 19, 2025
4a9ca01
added arc cloning changes
the-tushar-meesho Dec 19, 2025
273e040
made rust and go identical
the-tushar-meesho Dec 21, 2025
d150276
convert Vec<Arc<str>> to Vec<String>
the-tushar-meesho Dec 21, 2025
4860077
fixed keys_schema rust
the-tushar-meesho Dec 21, 2025
45071dd
change log
the-tushar-meesho Dec 21, 2025
8ac99b2
added changes
the-tushar-meesho Dec 21, 2025
4082556
added setting all the data at the time of calling
the-tushar-meesho Dec 23, 2025
5d9426f
reverted and make fg object in main
the-tushar-meesho Dec 23, 2025
6f15167
remove bug
the-tushar-meesho Dec 23, 2025
e3910ee
added rust profiling
the-tushar-meesho Dec 23, 2025
14618f6
rust profiling
the-tushar-meesho Dec 23, 2025
337f946
fix pprof changes
the-tushar-meesho Dec 23, 2025
9ec3c22
fix
the-tushar-meesho Dec 23, 2025
3f55c32
added
the-tushar-meesho Dec 23, 2025
3802a7b
added pprof fix
the-tushar-meesho Dec 23, 2025
55c20db
pprof fix
the-tushar-meesho Dec 23, 2025
3dbfd18
added changes
the-tushar-meesho Dec 23, 2025
584fa5a
added changes
the-tushar-meesho Dec 23, 2025
17e2e67
added
the-tushar-meesho Dec 23, 2025
c40ff0f
added changes
the-tushar-meesho Dec 23, 2025
539bf23
added
the-tushar-meesho Dec 23, 2025
24c40b9
added changes
the-tushar-meesho Dec 23, 2025
c1b570b
added pprof api
the-tushar-meesho Dec 23, 2025
04cc45b
added fix pprof
the-tushar-meesho Dec 23, 2025
88f8ec2
added pprof
the-tushar-meesho Dec 23, 2025
5cfe7e8
added pprof changes
the-tushar-meesho Dec 23, 2025
3aa8ac8
added pprof
the-tushar-meesho Dec 23, 2025
b75cde7
added pprof
the-tushar-meesho Dec 23, 2025
658419c
added pprof
the-tushar-meesho Dec 23, 2025
84f3b04
added pprof
the-tushar-meesho Dec 23, 2025
e207a9e
added pprof
the-tushar-meesho Dec 23, 2025
de15150
added retrieve features optimization
the-tushar-meesho Dec 23, 2025
bd6d8f4
added pprof
the-tushar-meesho Dec 23, 2025
4dd9c38
remove pprof for analysis
the-tushar-meesho Dec 23, 2025
ff338fc
change request
the-tushar-meesho Dec 23, 2025
bcf3272
added similar changes in go also
the-tushar-meesho Dec 24, 2025
8115ae7
remove thread ristriction
the-tushar-meesho Dec 24, 2025
c15f0ba
added client pooling
the-tushar-meesho Dec 26, 2025
8164fce
remove pooling and increase concurrency_limit
the-tushar-meesho Dec 26, 2025
9da6de7
increase _window_size and connection_window_size
the-tushar-meesho Dec 26, 2025
c4b9ede
increase concurrency_limit
the-tushar-meesho Dec 26, 2025
8ad3cc1
remove concurrency kimit and window size
the-tushar-meesho Dec 26, 2025
7f7486a
added hyper and remove axum
the-tushar-meesho Dec 28, 2025
9a2e8cc
remove worker thread
the-tushar-meesho Dec 28, 2025
8dbf784
zero copy
the-tushar-meesho Dec 28, 2025
8728155
refactor: replace Axum with raw Hyper and optimize zero-copy transfor…
the-tushar-meesho Dec 28, 2025
8740bef
remove pprof
the-tushar-meesho Dec 28, 2025
d3c3674
change main.rs body conversion
the-tushar-meesho Dec 29, 2025
c757d44
fix error
the-tushar-meesho Dec 29, 2025
4764eed
added _inner_data
the-tushar-meesho Dec 29, 2025
afcce92
added pprof changes
the-tushar-meesho Dec 29, 2025
e274c40
correct jemalloc_pprof package name
the-tushar-meesho Dec 29, 2025
7545c42
added pprof version update
the-tushar-meesho Dec 29, 2025
125d1d5
added pprof correction
the-tushar-meesho Dec 29, 2025
5cf9f65
added pprof correction
the-tushar-meesho Dec 29, 2025
c57290d
added pprof correction
the-tushar-meesho Dec 29, 2025
827b760
added jemallocator
the-tushar-meesho Dec 29, 2025
62e6e01
added pprof heap correction
the-tushar-meesho Dec 29, 2025
dfcd12d
added frame graph correction
the-tushar-meesho Dec 29, 2025
5228f32
added pprof
the-tushar-meesho Dec 29, 2025
b0a2989
optimized rust code
the-tushar-meesho Dec 29, 2025
ea6d90b
increased concurrency to 10000
the-tushar-meesho Dec 29, 2025
a5cf0b0
changed code remove pprof
the-tushar-meesho Dec 29, 2025
dc5e0f3
change for to 8080
the-tushar-meesho Dec 29, 2025
5ffc0dd
added zero copy code
the-tushar-meesho Dec 29, 2025
ba888e6
mutable fix
the-tushar-meesho Dec 29, 2025
20b2f24
use hyper over axum
the-tushar-meesho Dec 29, 2025
a587532
remove cpu utilizatiob
the-tushar-meesho Dec 29, 2025
429d059
added
the-tushar-meesho Dec 29, 2025
af45d1a
make correct
the-tushar-meesho Dec 29, 2025
fa5a827
added changes
the-tushar-meesho Dec 29, 2025
d305607
added performance fixes
the-tushar-meesho Dec 29, 2025
470f5b7
correct
the-tushar-meesho Dec 29, 2025
ecf6cdb
added bottleneck fix
the-tushar-meesho Dec 29, 2025
7c4677c
added some more to reduce cpu utilization
the-tushar-meesho Dec 30, 2025
5df15ee
added change
the-tushar-meesho Dec 30, 2025
24e7fda
increased go rps for http
the-tushar-meesho Dec 30, 2025
607a1e9
go connection pool changes
the-tushar-meesho Dec 30, 2025
cd255c1
increase keep alove
the-tushar-meesho Dec 30, 2025
9ed22a0
added channel changes
the-tushar-meesho Dec 30, 2025
fab1a8d
added best version of rust
the-tushar-meesho Dec 30, 2025
31c7418
added best version rust
the-tushar-meesho Dec 30, 2025
5c62f73
added go caller connection pull
the-tushar-meesho Dec 30, 2025
8591162
added go fix
the-tushar-meesho Dec 30, 2025
803c731
added go
the-tushar-meesho Dec 30, 2025
505fc48
added keep alive true
the-tushar-meesho Dec 30, 2025
da6bbc4
issue with port connection
the-tushar-meesho Dec 30, 2025
04e575e
increase timeout
the-tushar-meesho Dec 30, 2025
1c60c70
ADDED 3X
the-tushar-meesho Dec 30, 2025
3df6ed7
added stable go-caller
the-tushar-meesho Dec 30, 2025
02feea2
removed pooling
the-tushar-meesho Dec 31, 2025
92422f4
added go changes
the-tushar-meesho Dec 31, 2025
93e1b27
go test version 5
the-tushar-meesho Dec 31, 2025
eda81ce
hide response
the-tushar-meesho Dec 31, 2025
38b4d4b
added go and rust return response changes
the-tushar-meesho Dec 31, 2025
4090a19
remove profiling and added Server stopped gracefully
the-tushar-meesho Dec 31, 2025
99736c5
reduce time out to 5
the-tushar-meesho Dec 31, 2025
024e1d7
added connection pool rust
the-tushar-meesho Jan 1, 2026
0d17039
make clinet mutable
the-tushar-meesho Jan 1, 2026
c73e597
remove pooling and increase concurrency
the-tushar-meesho Jan 1, 2026
220eac6
remove concurrency
the-tushar-meesho Jan 1, 2026
51cf96d
added k6 latency test script
the-tushar-meesho Jan 2, 2026
a3e7aa3
added http3 changes
the-tushar-meesho Jan 5, 2026
cb071a4
fix tonic-h3 dependency
the-tushar-meesho Jan 5, 2026
36aa8fb
fix http3 issue
the-tushar-meesho Jan 5, 2026
1b7d16d
added go channel pool
the-tushar-meesho Jan 8, 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
285 changes: 195 additions & 90 deletions go-caller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,137 +2,242 @@ package main

import (
"context"
"fmt"
"log"
"net"
"net/http"
"os"
"os/signal"
"sync/atomic"
"syscall"
"time"

"github.com/gin-gonic/gin"
retrieve "github.com/Meesho/BharatMLStack/go-sdk/pkg/proto/onfs/retrieve" // adjust path
"github.com/gin-gonic/gin"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/keepalive"
"google.golang.org/grpc/metadata"
)

// ApiResponse matches your Rust ApiResponse struct
type ApiResponse struct {
Success bool `json:"success"`
Data *string `json:"data,omitempty"`
Error *string `json:"error,omitempty"`
Message string `json:"message"`
const (
// Connection pool size - each connection can handle ~100 concurrent streams
// With 16 connections, we can handle ~1600 concurrent requests
CONNECTION_POOL_SIZE = 16
)

// Request body structures for retrieve_features endpoint
type RetrieveFeaturesRequest struct {
EntityLabel string `json:"entity_label" binding:"required"`
FeatureGroups []FeatureGroupRequest `json:"feature_groups" binding:"required"`
KeysSchema []string `json:"keys_schema" binding:"required"`
Keys []KeysRequest `json:"keys" binding:"required"`
}

// AppState stores gRPC client
type AppState struct {
client retrieve.FeatureServiceClient
type FeatureGroupRequest struct {
Label string `json:"label" binding:"required"`
FeatureLabels []string `json:"feature_labels" binding:"required"`
}

// retrieveFeatures handles HTTP request
func (s *AppState) retrieveFeatures(c *gin.Context) {
authToken := "atishay"
callerID := "test-3"
type KeysRequest struct {
Cols []string `json:"cols" binding:"required"`
}

result, err := s.retrieveFeaturesInternal(authToken, callerID)
if err != nil {
log.Printf("❌ gRPC Error: %v", err)
errMsg := err.Error()
c.JSON(http.StatusInternalServerError, ApiResponse{
Success: false,
Data: nil,
Error: &errMsg,
Message: "Failed to retrieve features",
})
return
// ClientPool manages a pool of gRPC clients for connection pooling
type ClientPool struct {
clients []retrieve.FeatureServiceClient
conns []*grpc.ClientConn // Store connections for cleanup
counter uint64 // Atomic counter for round-robin selection
}

// Close closes all gRPC connections in the pool
func (p *ClientPool) Close() {
for _, conn := range p.conns {
if conn != nil {
if err := conn.Close(); err != nil {
log.Printf("Error closing gRPC connection: %v", err)
}
}
}
}

data := fmt.Sprintf("%v", result)
c.JSON(http.StatusOK, ApiResponse{
Success: true,
Data: &data,
Error: nil,
Message: "Features retrieved successfully",
})
// Next returns the next client from the pool using round-robin
func (p *ClientPool) Next() retrieve.FeatureServiceClient {
idx := atomic.AddUint64(&p.counter, 1) - 1
return p.clients[idx%uint64(len(p.clients))]
}

// retrieveFeaturesInternal calls gRPC backend
func (s *AppState) retrieveFeaturesInternal(authToken, callerID string) (*retrieve.Result, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
// AppState stores gRPC client pool and metadata
type AppState struct {
pool *ClientPool
metadata metadata.MD
}

func (s *AppState) handler(c *gin.Context) {
var requestBody RetrieveFeaturesRequest
if err := c.ShouldBindJSON(&requestBody); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

// Use request context instead of Background() for better cancellation handling
ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second)
defer cancel()
ctx = metadata.NewOutgoingContext(ctx, s.metadata)

// Convert request body to protobuf Query
featureGroups := make([]*retrieve.FeatureGroup, 0, len(requestBody.FeatureGroups))
for _, fg := range requestBody.FeatureGroups {
featureGroups = append(featureGroups, &retrieve.FeatureGroup{
Label: fg.Label,
FeatureLabels: fg.FeatureLabels,
})
}

// Attach metadata
md := metadata.New(map[string]string{
"online-feature-store-auth-token": authToken,
"online-feature-store-caller-id": callerID,
})
ctx = metadata.NewOutgoingContext(ctx, md)
keys := make([]*retrieve.Keys, 0, len(requestBody.Keys))
for _, k := range requestBody.Keys {
keys = append(keys, &retrieve.Keys{
Cols: k.Cols,
})
}

// Build gRPC request
req := &retrieve.Query{
EntityLabel: "catalog",
FeatureGroups: []*retrieve.FeatureGroup{
{
Label: "derived_fp32",
FeatureLabels: []string{
"clicks_by_views_3_days",
},
},
},
KeysSchema: []string{"catalog_id"},
Keys: []*retrieve.Keys{
{Cols: []string{"176"}},
{Cols: []string{"179"}},
},
EntityLabel: requestBody.EntityLabel,
FeatureGroups: featureGroups,
KeysSchema: requestBody.KeysSchema,
Keys: keys,
}

log.Println("πŸ“‘ Retrieving features...")
resp, err := s.client.RetrieveFeatures(ctx, req)
// Get next client from pool using round-robin
client := s.pool.Next()
_, err := client.RetrieveFeatures(ctx, req)
if err != nil {
return nil, err
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
return resp, nil
c.JSON(http.StatusOK, "success")
}

func main() {
log.Println("Connecting to feature store...")
log.Println("Starting go-caller with connection pooling (pool size:", CONNECTION_POOL_SIZE, ")")
gin.SetMode(gin.ReleaseMode)

// Create connection pool
clients := make([]retrieve.FeatureServiceClient, 0, CONNECTION_POOL_SIZE)
conns := make([]*grpc.ClientConn, 0, CONNECTION_POOL_SIZE)
for i := 0; i < CONNECTION_POOL_SIZE; i++ {
// Each Dial creates a separate connection
// Note: grpc.Dial is non-blocking - connections are established lazily on first use
conn, err := grpc.Dial(
"online-feature-store-api.int.meesho.int:80",
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithKeepaliveParams(keepalive.ClientParameters{
Time: 30 * time.Second,
Timeout: 10 * time.Second,
PermitWithoutStream: true,
}),
grpc.WithInitialWindowSize(2*1024*1024), // 2MB stream window
grpc.WithInitialConnWindowSize(4*1024*1024), // 4MB connection window
)
if err != nil {
log.Fatalf("Failed to create connection %d: %v", i, err)
}
conns = append(conns, conn)
clients = append(clients, retrieve.NewFeatureServiceClient(conn))
log.Printf("Created gRPC client %d/%d", i+1, CONNECTION_POOL_SIZE)
}
log.Println("Connection pool initialized with", CONNECTION_POOL_SIZE, "clients")

pool := &ClientPool{
clients: clients,
conns: conns,
counter: 0,
}

state := &AppState{
pool: pool,
metadata: metadata.MD{
"online-feature-store-auth-token": []string{"atishay"},
"online-feature-store-caller-id": []string{"test-3"},
},
}

r := gin.New()
r.POST("/retrieve-features", state.handler)

// Configure HTTP server for high concurrency with connection reuse
srv := &http.Server{
Addr: ":8081",
Handler: r,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 120 * time.Second,
MaxHeaderBytes: 1 << 20, // 1MB
}

// Enable HTTP keep-alive for connection reuse
// This prevents port exhaustion by reusing connections
// IdleTimeout (set above) controls the keep-alive period
srv.SetKeepAlivesEnabled(true)

// gRPC channel
conn, err := grpc.Dial(
"online-feature-store-api.int.meesho.int:80",
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
// Create listener with connection reuse settings
listener, err := net.Listen("tcp", ":8081")

@semgrep-sast-meesho semgrep-sast-meesho Bot Dec 30, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Detected a network listener listening on 0.0.0.0 or an empty string. This could unexpectedly expose the server publicly as it binds to all available interfaces. Instead, specify another IP address that is not 0.0.0.0 nor the empty string.

🧹 Fixed in commit 3df6ed7 🧹

if err != nil {
log.Fatalf("Failed to connect gRPC: %v", err)
log.Fatalf("Failed to create listener: %v", err)
}
defer conn.Close()

client := retrieve.NewFeatureServiceClient(conn)
state := &AppState{client: client}
// Use TCP keep-alive to detect dead connections
tcpListener := listener.(*net.TCPListener)
keepAliveListener := &keepAliveListener{
TCPListener: tcpListener,
KeepAlivePeriod: 30 * time.Second,
}

// Gin server
router := gin.Default()
// Setup graceful shutdown
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

// Allow CORS permissive (similar to Rust)
router.Use(func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS")
c.Writer.Header().Set("Access-Control-Allow-Headers", "*")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
return
go func() {
log.Println("πŸš€ Go gRPC Client running on http://0.0.0.0:8081")
if err := srv.Serve(keepAliveListener); err != nil && err != http.ErrServerClosed {
log.Fatalf("Failed to start server: %v", err)
}
c.Next()
})
}()

// Wait for interrupt signal
<-quit
log.Println("Shutting down server...")

router.POST("/retrieve-features", state.retrieveFeatures)
// Graceful shutdown with timeout
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second)
defer shutdownCancel()

port := "8081"
if os.Getenv("PORT") != "" {
port = os.Getenv("PORT")
if err := srv.Shutdown(shutdownCtx); err != nil {
log.Printf("Server forced to shutdown: %v", err)
}

log.Printf("πŸš€ Starting go-caller on http://0.0.0.0:%s\n", port)
if err := router.Run("0.0.0.0:" + port); err != nil {
log.Fatalf("Failed to start server: %v", err)
// Close all gRPC connections
pool.Close()
log.Println("Server exited")
}

// keepAliveListener wraps TCPListener to enable TCP keep-alive
type keepAliveListener struct {
*net.TCPListener
KeepAlivePeriod time.Duration
}

func (ln *keepAliveListener) Accept() (net.Conn, error) {
conn, err := ln.TCPListener.AcceptTCP()
if err != nil {
return nil, err
}
if err := conn.SetKeepAlive(true); err != nil {
conn.Close()
return nil, err
}
if err := conn.SetKeepAlivePeriod(ln.KeepAlivePeriod); err != nil {
conn.Close()
return nil, err
}
return conn, nil
}
Loading