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 }