 9bdcbe0447
			
		
	
	9bdcbe0447
	
	
	
		
			
			Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			133 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			133 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package httpu
 | |
| 
 | |
| import (
 | |
| 	"net/http"
 | |
| 	"time"
 | |
| 
 | |
| 	"golang.org/x/sync/errgroup"
 | |
| )
 | |
| 
 | |
| // MultiClient dispatches requests out to all the delegated clients.
 | |
| type MultiClient struct {
 | |
| 	// The HTTPU clients to delegate to.
 | |
| 	delegates []ClientInterface
 | |
| }
 | |
| 
 | |
| var _ ClientInterface = &MultiClient{}
 | |
| 
 | |
| // NewMultiClient creates a new MultiClient that delegates to all the given
 | |
| // clients.
 | |
| func NewMultiClient(delegates []ClientInterface) *MultiClient {
 | |
| 	return &MultiClient{
 | |
| 		delegates: delegates,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Do implements ClientInterface.Do.
 | |
| func (mc *MultiClient) Do(
 | |
| 	req *http.Request,
 | |
| 	timeout time.Duration,
 | |
| 	numSends int,
 | |
| ) ([]*http.Response, error) {
 | |
| 	tasks := &errgroup.Group{}
 | |
| 
 | |
| 	results := make(chan []*http.Response)
 | |
| 	tasks.Go(func() error {
 | |
| 		defer close(results)
 | |
| 		return mc.sendRequests(results, req, timeout, numSends)
 | |
| 	})
 | |
| 
 | |
| 	var responses []*http.Response
 | |
| 	tasks.Go(func() error {
 | |
| 		for rs := range results {
 | |
| 			responses = append(responses, rs...)
 | |
| 		}
 | |
| 		return nil
 | |
| 	})
 | |
| 
 | |
| 	return responses, tasks.Wait()
 | |
| }
 | |
| 
 | |
| func (mc *MultiClient) sendRequests(
 | |
| 	results chan<- []*http.Response,
 | |
| 	req *http.Request,
 | |
| 	timeout time.Duration,
 | |
| 	numSends int,
 | |
| ) error {
 | |
| 	tasks := &errgroup.Group{}
 | |
| 	for _, d := range mc.delegates {
 | |
| 		d := d // copy for closure
 | |
| 		tasks.Go(func() error {
 | |
| 			responses, err := d.Do(req, timeout, numSends)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			results <- responses
 | |
| 			return nil
 | |
| 		})
 | |
| 	}
 | |
| 	return tasks.Wait()
 | |
| }
 | |
| 
 | |
| // MultiClientCtx dispatches requests out to all the delegated clients.
 | |
| type MultiClientCtx struct {
 | |
| 	// The HTTPU clients to delegate to.
 | |
| 	delegates []ClientInterfaceCtx
 | |
| }
 | |
| 
 | |
| var _ ClientInterfaceCtx = &MultiClientCtx{}
 | |
| 
 | |
| // NewMultiClient creates a new MultiClient that delegates to all the given
 | |
| // clients.
 | |
| func NewMultiClientCtx(delegates []ClientInterfaceCtx) *MultiClientCtx {
 | |
| 	return &MultiClientCtx{
 | |
| 		delegates: delegates,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // DoWithContext implements ClientInterfaceCtx.DoWithContext.
 | |
| func (mc *MultiClientCtx) DoWithContext(
 | |
| 	req *http.Request,
 | |
| 	numSends int,
 | |
| ) ([]*http.Response, error) {
 | |
| 	tasks, ctx := errgroup.WithContext(req.Context())
 | |
| 	req = req.WithContext(ctx) // so we cancel if the errgroup errors
 | |
| 	results := make(chan []*http.Response)
 | |
| 
 | |
| 	// For each client, send the request to it and collect results.
 | |
| 	tasks.Go(func() error {
 | |
| 		defer close(results)
 | |
| 		return mc.sendRequestsCtx(results, req, numSends)
 | |
| 	})
 | |
| 
 | |
| 	var responses []*http.Response
 | |
| 	tasks.Go(func() error {
 | |
| 		for rs := range results {
 | |
| 			responses = append(responses, rs...)
 | |
| 		}
 | |
| 		return nil
 | |
| 	})
 | |
| 
 | |
| 	return responses, tasks.Wait()
 | |
| }
 | |
| 
 | |
| func (mc *MultiClientCtx) sendRequestsCtx(
 | |
| 	results chan<- []*http.Response,
 | |
| 	req *http.Request,
 | |
| 	numSends int,
 | |
| ) error {
 | |
| 	tasks := &errgroup.Group{}
 | |
| 	for _, d := range mc.delegates {
 | |
| 		d := d // copy for closure
 | |
| 		tasks.Go(func() error {
 | |
| 			responses, err := d.DoWithContext(req, numSends)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			results <- responses
 | |
| 			return nil
 | |
| 		})
 | |
| 	}
 | |
| 	return tasks.Wait()
 | |
| }
 |