package tree import ( "context" "fmt" "sync" grpcService "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool/tree/service" "google.golang.org/grpc" ) type treeClient struct { mu sync.RWMutex address string opts []grpc.DialOption conn *grpc.ClientConn service grpcService.TreeServiceClient healthy bool } // newTreeClient creates new tree client with auto dial. func newTreeClient(addr string, opts ...grpc.DialOption) *treeClient { return &treeClient{ address: addr, opts: opts, } } func (c *treeClient) dial(ctx context.Context) error { c.mu.Lock() defer c.mu.Unlock() if c.conn != nil { return fmt.Errorf("couldn't dial '%s': connection already established", c.address) } var err error c.conn, err = grpc.DialContext(ctx, c.address, c.opts...) if err != nil { return fmt.Errorf("grpc dial node tree service: %w", err) } c.service = grpcService.NewTreeServiceClient(c.conn) if _, err = c.service.Healthcheck(ctx, &grpcService.HealthcheckRequest{}); err != nil { return fmt.Errorf("healthcheck tree service: %w", err) } c.healthy = true return nil } func (c *treeClient) redialIfNecessary(ctx context.Context) (healthHasChanged bool, err error) { c.mu.Lock() defer c.mu.Unlock() if c.conn == nil { c.conn, err = grpc.DialContext(ctx, c.address, c.opts...) if err != nil { return false, fmt.Errorf("grpc dial node tree service: %w", err) } c.service = grpcService.NewTreeServiceClient(c.conn) } wasHealthy := c.healthy if _, err = c.service.Healthcheck(ctx, &grpcService.HealthcheckRequest{}); err != nil { c.healthy = false return wasHealthy, fmt.Errorf("healthcheck tree service: %w", err) } return !wasHealthy, nil } func (c *treeClient) serviceClient() (grpcService.TreeServiceClient, error) { c.mu.RLock() defer c.mu.RUnlock() if c.conn == nil || !c.healthy { return nil, fmt.Errorf("unhealthy endpoint: '%s'", c.address) } return c.service, nil } func (c *treeClient) endpoint() string { return c.address } func (c *treeClient) isHealthy() bool { c.mu.RLock() defer c.mu.RUnlock() return c.healthy } func (c *treeClient) setHealthy(val bool) { c.mu.Lock() defer c.mu.Unlock() c.healthy = val } func (c *treeClient) close() error { c.mu.Lock() defer c.mu.Unlock() if c.conn == nil { return nil } return c.conn.Close() }