github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/services/cd-service/pkg/repository/queue.go (about)

     1  /*This file is part of kuberpult.
     2  
     3  Kuberpult is free software: you can redistribute it and/or modify
     4  it under the terms of the Expat(MIT) License as published by
     5  the Free Software Foundation.
     6  
     7  Kuberpult is distributed in the hope that it will be useful,
     8  but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    10  MIT License for more details.
    11  
    12  You should have received a copy of the MIT License
    13  along with kuberpult. If not, see <https://directory.fsf.org/wiki/License:Expat>.
    14  
    15  Copyright 2023 freiheit.com*/
    16  
    17  package repository
    18  
    19  /**
    20  This queue contains transformers. Do not confuse with the "queuedVersion" field in protobuf (api.proto).
    21  The queue here is used because applying a change to git (pushing) takes some time.
    22  Still, every request waits for the transformer AND push to finish (that's what the `result` channel is for in the "transformerBatch struct" below).
    23  This queue improves the throughput when there are many parallel requests, because the "push" operation is done only once for multiple requests (a request here is essentially the same as a transformer).
    24  Many parallel requests can happen in a CI with many microservices that all call the "release" endpoint almost at the same time.
    25  This queue does not improve the latency, because each request still waits for the push to finish.
    26  */
    27  
    28  import (
    29  	"context"
    30  	"fmt"
    31  	"github.com/freiheit-com/kuberpult/pkg/logger"
    32  	"go.uber.org/zap"
    33  )
    34  
    35  type queue struct {
    36  	transformerBatches chan transformerBatch
    37  }
    38  
    39  var ErrQueueFull error
    40  
    41  type transformerBatch struct {
    42  	ctx          context.Context
    43  	transformers []Transformer
    44  	result       chan error
    45  }
    46  
    47  func (t *transformerBatch) finish(err error) {
    48  	select {
    49  	case t.result <- err:
    50  		close(t.result)
    51  	default:
    52  	}
    53  }
    54  
    55  func (q *queue) add(ctx context.Context, transformers []Transformer) <-chan error {
    56  	resultChannel := make(chan error, 1)
    57  	e := transformerBatch{
    58  		ctx:          ctx,
    59  		transformers: transformers,
    60  		result:       resultChannel,
    61  	}
    62  
    63  	select {
    64  	case q.transformerBatches <- e:
    65  		GaugeQueueSize(ctx, len(q.transformerBatches))
    66  		return resultChannel
    67  	default:
    68  		//Channel is full, we don't want to put anything else there.
    69  		ErrQueueFull = fmt.Errorf("queue is full. Queue Capacity: %d.", cap(q.transformerBatches))
    70  		e.finish(ErrQueueFull)
    71  		return resultChannel
    72  	}
    73  }
    74  
    75  func makeQueueN(size uint) queue {
    76  	return queue{
    77  		transformerBatches: make(chan transformerBatch, size),
    78  	}
    79  }
    80  
    81  func GaugeQueueSize(ctx context.Context, queueSize int) {
    82  	if ddMetrics != nil {
    83  		err := ddMetrics.Gauge("request_queue_size", float64(queueSize), []string{}, 1)
    84  
    85  		if err != nil {
    86  			logger.FromContext(ctx).Error("Error gauging queue size metric: ", zap.Error(err))
    87  		}
    88  	}
    89  }