Skip to content

[v2] Add IsReady method to App interface and implement connection checks#475

Merged
ffelipelimao merged 1 commit intov2from
feat/add-isready-function-v2
Jan 16, 2026
Merged

[v2] Add IsReady method to App interface and implement connection checks#475
ffelipelimao merged 1 commit intov2from
feat/add-isready-function-v2

Conversation

@ffelipelimao
Copy link
Collaborator

@ffelipelimao ffelipelimao commented Dec 18, 2025

Description

This PR introduces a new IsReady() method to the Pitaya interface that provides one step to make a health check mechanism to determine if the metagame/connector is ready to serve requests.

Motivation

  • Requests being routed to instances that haven't fully connected to required services (NATS, ETCD)
  • Failed requests during service startup
  • Difficulty implementing proper readiness probes in Kubernetes environments

Core Functionality

The IsReady() method validates:

  • Application Running State: Checks if app.IsRunning() returns true
  • NATS RPC Client Connection: Verifies NATS client is connected (if configured)
  • NATS RPC Server Connection: Verifies NATS server is connected (if configured)
    -ETCD Service Discovery: Validates ETCD connection health (if configured)

Usage Example

HTTP Server to Health Check example

//main.go
func initializeServer(flags *cluster.StartCommandFlags) {
	var (
		viper       = viper.GetViper()
		logger      = logging.NewLogger(gameID, flags.ServerType, logFormat, verbosity)
		fieldLogger = logging.NewFieldLogger(gameID, flags.ServerType, logFormat, verbosity)
	)

	fixViperEnvVars(viper)

	err := configureJaeger(viper)
	if err != nil {
		logger.WithError(err).Error("Could not configure Jaeger")
	}

	if flags.ServerType == servers.CS {
		servers.StartCSServer(viper, logger, verbosity)
		return
	}

	var app pitaya.Pitaya

	switch flags.ServerType {
	case servers.Connector:
		app = servers.NewConnectorServer(viper, logger, flags)
	case servers.Metagame:
		app = servers.NewMetagameServer(viper, logger, fieldLogger, verbosity)
	case servers.WorkerClanWars:
		app = servers.NewWorkerClanWars(viper, logger)
	case servers.WorkerSplitServer:
		app = servers.NewWorkerSplitServer(viper, logger)
	default:
		logger.Fatalf("Unknown server type: %s", flags.ServerType)
	}

	pitaya.SetLogger(logger)
	
	// HERE
	go startHealthServer(app, 8080, logger)
	
	app.Start()  // Blocking Operation
}
//health_server.go
func startHealthServer(app pitaya.Pitaya, port int, logger interface{}) {
	http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
		if app.IsRunning() {
			w.WriteHeader(http.StatusOK)
			fmt.Fprint(w, "alive")
		} else {
			w.WriteHeader(http.StatusServiceUnavailable)
			fmt.Fprint(w, "dead")
		}
	})

	http.HandleFunc("/readyz", func(w http.ResponseWriter, r *http.Request) {
		ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
		defer cancel()
                 //HERE
		if app.IsReady(ctx) {
			w.WriteHeader(http.StatusOK)
			fmt.Fprint(w, "ready")
		} else {
			w.WriteHeader(http.StatusServiceUnavailable)
			fmt.Fprint(w, "not ready")
		}
	})

	http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
		defer cancel()

		ready := app.IsReady(ctx)
		status := map[string]interface{}{
			"ready":             ready,
			"running":           app.IsRunning(),
			"connected_clients": app.GetNumberOfConnectedClients(),
		}

		w.Header().Set("Content-Type", "application/json")
		if ready {
			w.WriteHeader(http.StatusOK)
		} else {
			w.WriteHeader(http.StatusServiceUnavailable)
		}
		json.NewEncoder(w).Encode(status)
	})

	if log, ok := logger.(interface{ Infof(string, ...interface{}) }); ok {
		log.Infof("Health check server listening on :%d", port)
	}

	if err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil); err != nil {
		if log, ok := logger.(interface{ Errorf(string, ...interface{}) }); ok {
			log.Errorf("Health server error: %v", err)
		}
	}
}

@coveralls
Copy link

Pull Request Test Coverage Report for Build 20349108963

Details

  • 0 of 42 (0.0%) changed or added relevant lines in 5 files are covered.
  • 19 unchanged lines in 2 files lost coverage.
  • Overall coverage decreased (-0.5%) to 61.46%

Changes Missing Coverage Covered Lines Changed/Added Lines %
cluster/nats_rpc_client.go 0 3 0.0%
cluster/nats_rpc_server.go 0 3 0.0%
static.go 0 3 0.0%
cluster/etcd_service_discovery.go 0 10 0.0%
app.go 0 23 0.0%
Files with Coverage Reduction New Missed Lines %
module.go 6 62.64%
app.go 13 66.78%
Totals Coverage Status
Change from base Build 19577298026: -0.5%
Covered Lines: 5154
Relevant Lines: 8386

💛 - Coveralls

@ffelipelimao ffelipelimao changed the title Implement IsReady method for Pitaya interface and related checks for … [v2] Add IsReady method to App interface and implement connection checks Dec 18, 2025
Copy link
Contributor

@rsafonseca rsafonseca left a comment

Choose a reason for hiding this comment

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

LGTM :)

@ffelipelimao ffelipelimao marked this pull request as ready for review January 15, 2026 20:27
@ffelipelimao ffelipelimao merged commit 7343bca into v2 Jan 16, 2026
4 checks passed
@ffelipelimao ffelipelimao deleted the feat/add-isready-function-v2 branch January 16, 2026 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants