code.gitea.io/gitea@v1.19.3/modules/setting/queue.go (about)

     1  // Copyright 2019 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package setting
     5  
     6  import (
     7  	"path/filepath"
     8  	"strconv"
     9  	"time"
    10  
    11  	"code.gitea.io/gitea/modules/container"
    12  	"code.gitea.io/gitea/modules/log"
    13  
    14  	ini "gopkg.in/ini.v1"
    15  )
    16  
    17  // QueueSettings represent the settings for a queue from the ini
    18  type QueueSettings struct {
    19  	Name             string
    20  	DataDir          string
    21  	QueueLength      int `ini:"LENGTH"`
    22  	BatchLength      int
    23  	ConnectionString string
    24  	Type             string
    25  	QueueName        string
    26  	SetName          string
    27  	WrapIfNecessary  bool
    28  	MaxAttempts      int
    29  	Timeout          time.Duration
    30  	Workers          int
    31  	MaxWorkers       int
    32  	BlockTimeout     time.Duration
    33  	BoostTimeout     time.Duration
    34  	BoostWorkers     int
    35  }
    36  
    37  // Queue settings
    38  var Queue = QueueSettings{}
    39  
    40  // GetQueueSettings returns the queue settings for the appropriately named queue
    41  func GetQueueSettings(name string) QueueSettings {
    42  	return getQueueSettings(CfgProvider, name)
    43  }
    44  
    45  func getQueueSettings(rootCfg ConfigProvider, name string) QueueSettings {
    46  	q := QueueSettings{}
    47  	sec := rootCfg.Section("queue." + name)
    48  	q.Name = name
    49  
    50  	// DataDir is not directly inheritable
    51  	q.DataDir = filepath.ToSlash(filepath.Join(Queue.DataDir, "common"))
    52  	// QueueName is not directly inheritable either
    53  	q.QueueName = name + Queue.QueueName
    54  	for _, key := range sec.Keys() {
    55  		switch key.Name() {
    56  		case "DATADIR":
    57  			q.DataDir = key.MustString(q.DataDir)
    58  		case "QUEUE_NAME":
    59  			q.QueueName = key.MustString(q.QueueName)
    60  		case "SET_NAME":
    61  			q.SetName = key.MustString(q.SetName)
    62  		}
    63  	}
    64  	if len(q.SetName) == 0 && len(Queue.SetName) > 0 {
    65  		q.SetName = q.QueueName + Queue.SetName
    66  	}
    67  	if !filepath.IsAbs(q.DataDir) {
    68  		q.DataDir = filepath.ToSlash(filepath.Join(AppDataPath, q.DataDir))
    69  	}
    70  	_, _ = sec.NewKey("DATADIR", q.DataDir)
    71  
    72  	// The rest are...
    73  	q.QueueLength = sec.Key("LENGTH").MustInt(Queue.QueueLength)
    74  	q.BatchLength = sec.Key("BATCH_LENGTH").MustInt(Queue.BatchLength)
    75  	q.ConnectionString = sec.Key("CONN_STR").MustString(Queue.ConnectionString)
    76  	q.Type = sec.Key("TYPE").MustString(Queue.Type)
    77  	q.WrapIfNecessary = sec.Key("WRAP_IF_NECESSARY").MustBool(Queue.WrapIfNecessary)
    78  	q.MaxAttempts = sec.Key("MAX_ATTEMPTS").MustInt(Queue.MaxAttempts)
    79  	q.Timeout = sec.Key("TIMEOUT").MustDuration(Queue.Timeout)
    80  	q.Workers = sec.Key("WORKERS").MustInt(Queue.Workers)
    81  	q.MaxWorkers = sec.Key("MAX_WORKERS").MustInt(Queue.MaxWorkers)
    82  	q.BlockTimeout = sec.Key("BLOCK_TIMEOUT").MustDuration(Queue.BlockTimeout)
    83  	q.BoostTimeout = sec.Key("BOOST_TIMEOUT").MustDuration(Queue.BoostTimeout)
    84  	q.BoostWorkers = sec.Key("BOOST_WORKERS").MustInt(Queue.BoostWorkers)
    85  
    86  	return q
    87  }
    88  
    89  // LoadQueueSettings sets up the default settings for Queues
    90  // This is exported for tests to be able to use the queue
    91  func LoadQueueSettings() {
    92  	loadQueueFrom(CfgProvider)
    93  }
    94  
    95  func loadQueueFrom(rootCfg ConfigProvider) {
    96  	sec := rootCfg.Section("queue")
    97  	Queue.DataDir = filepath.ToSlash(sec.Key("DATADIR").MustString("queues/"))
    98  	if !filepath.IsAbs(Queue.DataDir) {
    99  		Queue.DataDir = filepath.ToSlash(filepath.Join(AppDataPath, Queue.DataDir))
   100  	}
   101  	Queue.QueueLength = sec.Key("LENGTH").MustInt(20)
   102  	Queue.BatchLength = sec.Key("BATCH_LENGTH").MustInt(20)
   103  	Queue.ConnectionString = sec.Key("CONN_STR").MustString("")
   104  	defaultType := sec.Key("TYPE").String()
   105  	Queue.Type = sec.Key("TYPE").MustString("persistable-channel")
   106  	Queue.WrapIfNecessary = sec.Key("WRAP_IF_NECESSARY").MustBool(true)
   107  	Queue.MaxAttempts = sec.Key("MAX_ATTEMPTS").MustInt(10)
   108  	Queue.Timeout = sec.Key("TIMEOUT").MustDuration(GracefulHammerTime + 30*time.Second)
   109  	Queue.Workers = sec.Key("WORKERS").MustInt(0)
   110  	Queue.MaxWorkers = sec.Key("MAX_WORKERS").MustInt(10)
   111  	Queue.BlockTimeout = sec.Key("BLOCK_TIMEOUT").MustDuration(1 * time.Second)
   112  	Queue.BoostTimeout = sec.Key("BOOST_TIMEOUT").MustDuration(5 * time.Minute)
   113  	Queue.BoostWorkers = sec.Key("BOOST_WORKERS").MustInt(1)
   114  	Queue.QueueName = sec.Key("QUEUE_NAME").MustString("_queue")
   115  	Queue.SetName = sec.Key("SET_NAME").MustString("")
   116  
   117  	// Now handle the old issue_indexer configuration
   118  	// FIXME: DEPRECATED to be removed in v1.18.0
   119  	section := rootCfg.Section("queue.issue_indexer")
   120  	directlySet := toDirectlySetKeysSet(section)
   121  	if !directlySet.Contains("TYPE") && defaultType == "" {
   122  		switch typ := rootCfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_TYPE").MustString(""); typ {
   123  		case "levelqueue":
   124  			_, _ = section.NewKey("TYPE", "level")
   125  		case "channel":
   126  			_, _ = section.NewKey("TYPE", "persistable-channel")
   127  		case "redis":
   128  			_, _ = section.NewKey("TYPE", "redis")
   129  		case "":
   130  			_, _ = section.NewKey("TYPE", "level")
   131  		default:
   132  			log.Fatal("Unsupported indexer queue type: %v", typ)
   133  		}
   134  	}
   135  	if !directlySet.Contains("LENGTH") {
   136  		length := rootCfg.Section("indexer").Key("UPDATE_BUFFER_LEN").MustInt(0)
   137  		if length != 0 {
   138  			_, _ = section.NewKey("LENGTH", strconv.Itoa(length))
   139  		}
   140  	}
   141  	if !directlySet.Contains("BATCH_LENGTH") {
   142  		fallback := rootCfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_BATCH_NUMBER").MustInt(0)
   143  		if fallback != 0 {
   144  			_, _ = section.NewKey("BATCH_LENGTH", strconv.Itoa(fallback))
   145  		}
   146  	}
   147  	if !directlySet.Contains("DATADIR") {
   148  		queueDir := filepath.ToSlash(rootCfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_DIR").MustString(""))
   149  		if queueDir != "" {
   150  			_, _ = section.NewKey("DATADIR", queueDir)
   151  		}
   152  	}
   153  	if !directlySet.Contains("CONN_STR") {
   154  		connStr := rootCfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_CONN_STR").MustString("")
   155  		if connStr != "" {
   156  			_, _ = section.NewKey("CONN_STR", connStr)
   157  		}
   158  	}
   159  
   160  	// FIXME: DEPRECATED to be removed in v1.18.0
   161  	// - will need to set default for [queue.*)] LENGTH appropriately though though
   162  
   163  	// Handle the old mailer configuration
   164  	handleOldLengthConfiguration(rootCfg, "mailer", "mailer", "SEND_BUFFER_LEN", 100)
   165  
   166  	// Handle the old test pull requests configuration
   167  	// Please note this will be a unique queue
   168  	handleOldLengthConfiguration(rootCfg, "pr_patch_checker", "repository", "PULL_REQUEST_QUEUE_LENGTH", 1000)
   169  
   170  	// Handle the old mirror queue configuration
   171  	// Please note this will be a unique queue
   172  	handleOldLengthConfiguration(rootCfg, "mirror", "repository", "MIRROR_QUEUE_LENGTH", 1000)
   173  }
   174  
   175  // handleOldLengthConfiguration allows fallback to older configuration. `[queue.name]` `LENGTH` will override this configuration, but
   176  // if that is left unset then we should fallback to the older configuration. (Except where the new length woul be <=0)
   177  func handleOldLengthConfiguration(rootCfg ConfigProvider, queueName, oldSection, oldKey string, defaultValue int) {
   178  	if rootCfg.Section(oldSection).HasKey(oldKey) {
   179  		log.Error("Deprecated fallback for %s queue length `[%s]` `%s` present. Use `[queue.%s]` `LENGTH`. This will be removed in v1.18.0", queueName, queueName, oldSection, oldKey)
   180  	}
   181  	value := rootCfg.Section(oldSection).Key(oldKey).MustInt(defaultValue)
   182  
   183  	// Don't override with 0
   184  	if value <= 0 {
   185  		return
   186  	}
   187  
   188  	section := rootCfg.Section("queue." + queueName)
   189  	directlySet := toDirectlySetKeysSet(section)
   190  	if !directlySet.Contains("LENGTH") {
   191  		_, _ = section.NewKey("LENGTH", strconv.Itoa(value))
   192  	}
   193  }
   194  
   195  // toDirectlySetKeysSet returns a set of keys directly set by this section
   196  // Note: we cannot use section.HasKey(...) as that will immediately set the Key if a parent section has the Key
   197  // but this section does not.
   198  func toDirectlySetKeysSet(section *ini.Section) container.Set[string] {
   199  	sections := make(container.Set[string])
   200  	for _, key := range section.Keys() {
   201  		sections.Add(key.Name())
   202  	}
   203  	return sections
   204  }