Use global lock instead of NewExclusivePool to allow distributed lock between multiple Gitea instances (#31813)

Replace #26486 
Fix #19620

---------

Co-authored-by: Jason Song <i@wolfogre.com>
This commit is contained in:
Lunny XiaoandJason Song authored and GitHub committed 2024-09-06 10:12:41 +00:00
1 parent a5818470fe
commit 2da2000413
13 files changed
+185 -107

No files matched your search

+15
View File
@@ -489,6 +489,11 @@
"path": "github.com/go-ldap/ldap/v3/LICENSE", "path": "github.com/go-ldap/ldap/v3/LICENSE",
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com)\nPortions copyright (c) 2015-2016 go-ldap Authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com)\nPortions copyright (c) 2015-2016 go-ldap Authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
}, },
{
"name": "github.com/go-redsync/redsync/v4",
"path": "github.com/go-redsync/redsync/v4/LICENSE",
"licenseText": "Copyright (c) 2023, Mahmud Ridwan\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n* Neither the name of the Redsync nor the names of its\n contributors may be used to endorse or promote products derived from\n this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
},
{ {
"name": "github.com/go-sql-driver/mysql", "name": "github.com/go-sql-driver/mysql",
"path": "github.com/go-sql-driver/mysql/LICENSE", "path": "github.com/go-sql-driver/mysql/LICENSE",
@@ -624,11 +629,21 @@
"path": "github.com/gorilla/sessions/LICENSE", "path": "github.com/gorilla/sessions/LICENSE",
"licenseText": "Copyright (c) 2023 The Gorilla Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n\t * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n\t * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n\t * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" "licenseText": "Copyright (c) 2023 The Gorilla Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n\t * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n\t * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n\t * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
}, },
{
"name": "github.com/hashicorp/errwrap",
"path": "github.com/hashicorp/errwrap/LICENSE",
"licenseText": "Mozilla Public License, version 2.0\n\n1. Definitions\n\n1.1. “Contributor”\n\n means each individual or legal entity that creates, contributes to the\n creation of, or owns Covered Software.\n\n1.2. “Contributor Version”\n\n means the combination of the Contributions of others (if any) used by a\n Contributor and that particular Contributor’s Contribution.\n\n1.3. “Contribution”\n\n means Covered Software of a particular Contributor.\n\n1.4. “Covered Software”\n\n means Source Code Form to which the initial Contributor has attached the\n notice in Exhibit A, the Executable Form of such Source Code Form, and\n Modifications of such Source Code Form, in each case including portions\n thereof.\n\n1.5. “Incompatible With Secondary Licenses”\n means\n\n a. that the initial Contributor has attached the notice described in\n Exhibit B to the Covered Software; or\n\n b. that the Covered Software was made available under the terms of version\n 1.1 or earlier of the License, but not also under the terms of a\n Secondary License.\n\n1.6. “Executable Form”\n\n means any form of the work other than Source Code Form.\n\n1.7. “Larger Work”\n\n means a work that combines Covered Software with other material, in a separate\n file or files, that is not Covered Software.\n\n1.8. “License”\n\n means this document.\n\n1.9. “Licensable”\n\n means having the right to grant, to the maximum extent possible, whether at the\n time of the initial grant or subsequently, any and all of the rights conveyed by\n this License.\n\n1.10. “Modifications”\n\n means any of the following:\n\n a. any file in Source Code Form that results from an addition to, deletion\n from, or modification of the contents of Covered Software; or\n\n b. any new file in Source Code Form that contains any Covered Software.\n\n1.11. “Patent Claims” of a Contributor\n\n means any patent claim(s), including without limitation, method, process,\n and apparatus claims, in any patent Licensable by such Contributor that\n would be infringed, but for the grant of the License, by the making,\n using, selling, offering for sale, having made, import, or transfer of\n either its Contributions or its Contributor Version.\n\n1.12. “Secondary License”\n\n means either the GNU General Public License, Version 2.0, the GNU Lesser\n General Public License, Version 2.1, the GNU Affero General Public\n License, Version 3.0, or any later versions of those licenses.\n\n1.13. “Source Code Form”\n\n means the form of the work preferred for making modifications.\n\n1.14. “You” (or “Your”)\n\n means an individual or a legal entity exercising rights under this\n License. For legal entities, “You” includes any entity that controls, is\n controlled by, or is under common control with You. For purposes of this\n definition, “control” means (a) the power, direct or indirect, to cause\n the direction or management of such entity, whether by contract or\n otherwise, or (b) ownership of more than fifty percent (50%) of the\n outstanding shares or beneficial ownership of such entity.\n\n\n2. License Grants and Conditions\n\n2.1. Grants\n\n Each Contributor hereby grants You a world-wide, royalty-free,\n non-exclusive license:\n\n a. under intellectual property rights (other than patent or trademark)\n Licensable by such Contributor to use, reproduce, make available,\n modify, display, perform, distribute, and otherwise exploit its\n Contributions, either on an unmodified basis, with Modifications, or as\n part of a Larger Work; and\n\n b. under Patent Claims of such Contributor to make, use, sell, offer for\n sale, have made, import, and otherwise transfer either its Contributions\n or its Contributor Version.\n\n2.2. Effective Date\n\n The licenses granted in Section 2.1 with respect to any Contribution become\n effective for each Contribution on the date the Contributor first distributes\n such Contribution.\n\n2.3. Limitations on Grant Scope\n\n The licenses granted in this Section 2 are the only rights granted under this\n License. No additional rights or licenses will be implied from the distribution\n or licensing of Covered Software under this License. Notwithstanding Section\n 2.1(b) above, no patent license is granted by a Contributor:\n\n a. for any code that a Contributor has removed from Covered Software; or\n\n b. for infringements caused by: (i) Your and any other third party’s\n modifications of Covered Software, or (ii) the combination of its\n Contributions with other software (except as part of its Contributor\n Version); or\n\n c. under Patent Claims infringed by Covered Software Line truncated
},
{ {
"name": "github.com/hashicorp/go-cleanhttp", "name": "github.com/hashicorp/go-cleanhttp",
"path": "github.com/hashicorp/go-cleanhttp/LICENSE", "path": "github.com/hashicorp/go-cleanhttp/LICENSE",
"licenseText": "Mozilla Public License, version 2.0\n\n1. Definitions\n\n1.1. \"Contributor\"\n\n means each individual or legal entity that creates, contributes to the\n creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n\n means the combination of the Contributions of others (if any) used by a\n Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n\n means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n\n means Source Code Form to which the initial Contributor has attached the\n notice in Exhibit A, the Executable Form of such Source Code Form, and\n Modifications of such Source Code Form, in each case including portions\n thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n means\n\n a. that the initial Contributor has attached the notice described in\n Exhibit B to the Covered Software; or\n\n b. that the Covered Software was made available under the terms of\n version 1.1 or earlier of the License, but not also under the terms of\n a Secondary License.\n\n1.6. \"Executable Form\"\n\n means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n\n means a work that combines Covered Software with other material, in a\n separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n\n means this document.\n\n1.9. \"Licensable\"\n\n means having the right to grant, to the maximum extent possible, whether\n at the time of the initial grant or subsequently, any and all of the\n rights conveyed by this License.\n\n1.10. \"Modifications\"\n\n means any of the following:\n\n a. any file in Source Code Form that results from an addition to,\n deletion from, or modification of the contents of Covered Software; or\n\n b. any new file in Source Code Form that contains any Covered Software.\n\n1.11. \"Patent Claims\" of a Contributor\n\n means any patent claim(s), including without limitation, method,\n process, and apparatus claims, in any patent Licensable by such\n Contributor that would be infringed, but for the grant of the License,\n by the making, using, selling, offering for sale, having made, import,\n or transfer of either its Contributions or its Contributor Version.\n\n1.12. \"Secondary License\"\n\n means either the GNU General Public License, Version 2.0, the GNU Lesser\n General Public License, Version 2.1, the GNU Affero General Public\n License, Version 3.0, or any later versions of those licenses.\n\n1.13. \"Source Code Form\"\n\n means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n\n means an individual or a legal entity exercising rights under this\n License. For legal entities, \"You\" includes any entity that controls, is\n controlled by, or is under common control with You. For purposes of this\n definition, \"control\" means (a) the power, direct or indirect, to cause\n the direction or management of such entity, whether by contract or\n otherwise, or (b) ownership of more than fifty percent (50%) of the\n outstanding shares or beneficial ownership of such entity.\n\n\n2. License Grants and Conditions\n\n2.1. Grants\n\n Each Contributor hereby grants You a world-wide, royalty-free,\n non-exclusive license:\n\n a. under intellectual property rights (other than patent or trademark)\n Licensable by such Contributor to use, reproduce, make available,\n modify, display, perform, distribute, and otherwise exploit its\n Contributions, either on an unmodified basis, with Modifications, or\n as part of a Larger Work; and\n\n b. under Patent Claims of such Contributor to make, use, sell, offer for\n sale, have made, import, and otherwise transfer either its\n Contributions or its Contributor Version.\n\n2.2. Effective Date\n\n The licenses granted in Section 2.1 with respect to any Contribution\n become effective for each Contribution on the date the Contributor first\n distributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\n The licenses granted in this Section 2 are the only rights granted under\n this License. No additional rights or licenses will be implied from the\n distribution or licensing of Covered Software under this License.\n Notwithstanding Section 2.1(b) above, no patent license is granted by a\n Contributor:\n\n a. for any code that a Contributor has removed from Covered Software; or\n\n b. for infringements caused by: (i) Your and any other third party's\n modifications of Covered Software, or (ii) the combination of its\n Contributions with other software (except as part of its Contributor\n Version); or\n\n c. under Patent Claims infringed by Covered Software in the absence of\n its CLine truncated "licenseText": "Mozilla Public License, version 2.0\n\n1. Definitions\n\n1.1. \"Contributor\"\n\n means each individual or legal entity that creates, contributes to the\n creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n\n means the combination of the Contributions of others (if any) used by a\n Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n\n means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n\n means Source Code Form to which the initial Contributor has attached the\n notice in Exhibit A, the Executable Form of such Source Code Form, and\n Modifications of such Source Code Form, in each case including portions\n thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n means\n\n a. that the initial Contributor has attached the notice described in\n Exhibit B to the Covered Software; or\n\n b. that the Covered Software was made available under the terms of\n version 1.1 or earlier of the License, but not also under the terms of\n a Secondary License.\n\n1.6. \"Executable Form\"\n\n means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n\n means a work that combines Covered Software with other material, in a\n separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n\n means this document.\n\n1.9. \"Licensable\"\n\n means having the right to grant, to the maximum extent possible, whether\n at the time of the initial grant or subsequently, any and all of the\n rights conveyed by this License.\n\n1.10. \"Modifications\"\n\n means any of the following:\n\n a. any file in Source Code Form that results from an addition to,\n deletion from, or modification of the contents of Covered Software; or\n\n b. any new file in Source Code Form that contains any Covered Software.\n\n1.11. \"Patent Claims\" of a Contributor\n\n means any patent claim(s), including without limitation, method,\n process, and apparatus claims, in any patent Licensable by such\n Contributor that would be infringed, but for the grant of the License,\n by the making, using, selling, offering for sale, having made, import,\n or transfer of either its Contributions or its Contributor Version.\n\n1.12. \"Secondary License\"\n\n means either the GNU General Public License, Version 2.0, the GNU Lesser\n General Public License, Version 2.1, the GNU Affero General Public\n License, Version 3.0, or any later versions of those licenses.\n\n1.13. \"Source Code Form\"\n\n means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n\n means an individual or a legal entity exercising rights under this\n License. For legal entities, \"You\" includes any entity that controls, is\n controlled by, or is under common control with You. For purposes of this\n definition, \"control\" means (a) the power, direct or indirect, to cause\n the direction or management of such entity, whether by contract or\n otherwise, or (b) ownership of more than fifty percent (50%) of the\n outstanding shares or beneficial ownership of such entity.\n\n\n2. License Grants and Conditions\n\n2.1. Grants\n\n Each Contributor hereby grants You a world-wide, royalty-free,\n non-exclusive license:\n\n a. under intellectual property rights (other than patent or trademark)\n Licensable by such Contributor to use, reproduce, make available,\n modify, display, perform, distribute, and otherwise exploit its\n Contributions, either on an unmodified basis, with Modifications, or\n as part of a Larger Work; and\n\n b. under Patent Claims of such Contributor to make, use, sell, offer for\n sale, have made, import, and otherwise transfer either its\n Contributions or its Contributor Version.\n\n2.2. Effective Date\n\n The licenses granted in Section 2.1 with respect to any Contribution\n become effective for each Contribution on the date the Contributor first\n distributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\n The licenses granted in this Section 2 are the only rights granted under\n this License. No additional rights or licenses will be implied from the\n distribution or licensing of Covered Software under this License.\n Notwithstanding Section 2.1(b) above, no patent license is granted by a\n Contributor:\n\n a. for any code that a Contributor has removed from Covered Software; or\n\n b. for infringements caused by: (i) Your and any other third party's\n modifications of Covered Software, or (ii) the combination of its\n Contributions with other software (except as part of its Contributor\n Version); or\n\n c. under Patent Claims infringed by Covered Software in the absence of\n its CLine truncated
}, },
{
"name": "github.com/hashicorp/go-multierror",
"path": "github.com/hashicorp/go-multierror/LICENSE",
"licenseText": "Mozilla Public License, version 2.0\n\n1. Definitions\n\n1.1. “Contributor”\n\n means each individual or legal entity that creates, contributes to the\n creation of, or owns Covered Software.\n\n1.2. “Contributor Version”\n\n means the combination of the Contributions of others (if any) used by a\n Contributor and that particular Contributor’s Contribution.\n\n1.3. “Contribution”\n\n means Covered Software of a particular Contributor.\n\n1.4. “Covered Software”\n\n means Source Code Form to which the initial Contributor has attached the\n notice in Exhibit A, the Executable Form of such Source Code Form, and\n Modifications of such Source Code Form, in each case including portions\n thereof.\n\n1.5. “Incompatible With Secondary Licenses”\n means\n\n a. that the initial Contributor has attached the notice described in\n Exhibit B to the Covered Software; or\n\n b. that the Covered Software was made available under the terms of version\n 1.1 or earlier of the License, but not also under the terms of a\n Secondary License.\n\n1.6. “Executable Form”\n\n means any form of the work other than Source Code Form.\n\n1.7. “Larger Work”\n\n means a work that combines Covered Software with other material, in a separate\n file or files, that is not Covered Software.\n\n1.8. “License”\n\n means this document.\n\n1.9. “Licensable”\n\n means having the right to grant, to the maximum extent possible, whether at the\n time of the initial grant or subsequently, any and all of the rights conveyed by\n this License.\n\n1.10. “Modifications”\n\n means any of the following:\n\n a. any file in Source Code Form that results from an addition to, deletion\n from, or modification of the contents of Covered Software; or\n\n b. any new file in Source Code Form that contains any Covered Software.\n\n1.11. “Patent Claims” of a Contributor\n\n means any patent claim(s), including without limitation, method, process,\n and apparatus claims, in any patent Licensable by such Contributor that\n would be infringed, but for the grant of the License, by the making,\n using, selling, offering for sale, having made, import, or transfer of\n either its Contributions or its Contributor Version.\n\n1.12. “Secondary License”\n\n means either the GNU General Public License, Version 2.0, the GNU Lesser\n General Public License, Version 2.1, the GNU Affero General Public\n License, Version 3.0, or any later versions of those licenses.\n\n1.13. “Source Code Form”\n\n means the form of the work preferred for making modifications.\n\n1.14. “You” (or “Your”)\n\n means an individual or a legal entity exercising rights under this\n License. For legal entities, “You” includes any entity that controls, is\n controlled by, or is under common control with You. For purposes of this\n definition, “control” means (a) the power, direct or indirect, to cause\n the direction or management of such entity, whether by contract or\n otherwise, or (b) ownership of more than fifty percent (50%) of the\n outstanding shares or beneficial ownership of such entity.\n\n\n2. License Grants and Conditions\n\n2.1. Grants\n\n Each Contributor hereby grants You a world-wide, royalty-free,\n non-exclusive license:\n\n a. under intellectual property rights (other than patent or trademark)\n Licensable by such Contributor to use, reproduce, make available,\n modify, display, perform, distribute, and otherwise exploit its\n Contributions, either on an unmodified basis, with Modifications, or as\n part of a Larger Work; and\n\n b. under Patent Claims of such Contributor to make, use, sell, offer for\n sale, have made, import, and otherwise transfer either its Contributions\n or its Contributor Version.\n\n2.2. Effective Date\n\n The licenses granted in Section 2.1 with respect to any Contribution become\n effective for each Contribution on the date the Contributor first distributes\n such Contribution.\n\n2.3. Limitations on Grant Scope\n\n The licenses granted in this Section 2 are the only rights granted under this\n License. No additional rights or licenses will be implied from the distribution\n or licensing of Covered Software under this License. Notwithstanding Section\n 2.1(b) above, no patent license is granted by a Contributor:\n\n a. for any code that a Contributor has removed from Covered Software; or\n\n b. for infringements caused by: (i) Your and any other third party’s\n modifications of Covered Software, or (ii) the combination of its\n Contributions with other software (except as part of its Contributor\n Version); or\n\n c. under Patent Claims infringed by Covered Software Line truncated
},
{ {
"name": "github.com/hashicorp/go-retryablehttp", "name": "github.com/hashicorp/go-retryablehttp",
"path": "github.com/hashicorp/go-retryablehttp/LICENSE", "path": "github.com/hashicorp/go-retryablehttp/LICENSE",
+6
View File
@@ -2713,3 +2713,9 @@ LEVEL = Info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; storage type ;; storage type
;STORAGE_TYPE = local ;STORAGE_TYPE = local
;[global_lock]
;; Lock service type, could be memory or redis
;SERVICE_TYPE = memory
;; Ignored for the "memory" type. For "redis" use something like `redis://127.0.0.1:6379/0`
;SERVICE_CONN_STR =
+10 -2
View File
@@ -6,14 +6,22 @@ package globallock
import ( import (
"context" "context"
"sync" "sync"
"code.gitea.io/gitea/modules/setting"
) )
var ( var (
defaultLocker Locker defaultLocker Locker
initOnce sync.Once initOnce sync.Once
initFunc = func() { initFunc = func() {
// TODO: read the setting and initialize the default locker. switch setting.GlobalLock.ServiceType {
// Before implementing this, don't use it. case "redis":
defaultLocker = NewRedisLocker(setting.GlobalLock.ServiceConnStr)
case "memory":
fallthrough
default:
defaultLocker = NewMemoryLocker()
}
} // define initFunc as a variable to make it possible to change it in tests } // define initFunc as a variable to make it possible to change it in tests
) )
+37
View File
@@ -0,0 +1,37 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/nosql"
)
// GlobalLock represents configuration of global lock
var GlobalLock = struct {
ServiceType string
ServiceConnStr string
}{
ServiceType: "memory",
}
func loadGlobalLockFrom(rootCfg ConfigProvider) {
sec := rootCfg.Section("global_lock")
GlobalLock.ServiceType = sec.Key("SERVICE_TYPE").MustString("memory")
switch GlobalLock.ServiceType {
case "memory":
case "redis":
connStr := sec.Key("SERVICE_CONN_STR").String()
if connStr == "" {
log.Fatal("SERVICE_CONN_STR is empty for redis")
}
u := nosql.ToRedisURI(connStr)
if u == nil {
log.Fatal("SERVICE_CONN_STR %s is not a valid redis connection string", connStr)
}
GlobalLock.ServiceConnStr = connStr
default:
log.Fatal("Unknown sync lock service type: %s", GlobalLock.ServiceType)
}
}
+35
View File
@@ -0,0 +1,35 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestLoadGlobalLockConfig(t *testing.T) {
t.Run("DefaultGlobalLockConfig", func(t *testing.T) {
iniStr := ``
cfg, err := NewConfigProviderFromData(iniStr)
assert.NoError(t, err)
loadGlobalLockFrom(cfg)
assert.EqualValues(t, "memory", GlobalLock.ServiceType)
})
t.Run("RedisGlobalLockConfig", func(t *testing.T) {
iniStr := `
[global_lock]
SERVICE_TYPE = redis
SERVICE_CONN_STR = addrs=127.0.0.1:6379 db=0
`
cfg, err := NewConfigProviderFromData(iniStr)
assert.NoError(t, err)
loadGlobalLockFrom(cfg)
assert.EqualValues(t, "redis", GlobalLock.ServiceType)
assert.EqualValues(t, "addrs=127.0.0.1:6379 db=0", GlobalLock.ServiceConnStr)
})
}
+1
View File
@@ -147,6 +147,7 @@ func loadCommonSettingsFrom(cfg ConfigProvider) error {
loadGitFrom(cfg) loadGitFrom(cfg)
loadMirrorFrom(cfg) loadMirrorFrom(cfg)
loadMarkupFrom(cfg) loadMarkupFrom(cfg)
loadGlobalLockFrom(cfg)
loadOtherFrom(cfg) loadOtherFrom(cfg)
return nil return nil
} }
-69
View File
@@ -1,69 +0,0 @@
// Copyright 2016 The Gogs Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package sync
import (
"sync"
)
// ExclusivePool is a pool of non-identical instances
// that only one instance with same identity is in the pool at a time.
// In other words, only instances with different identities can be in
// the pool the same time. If another instance with same identity tries
// to get into the pool, it hangs until previous instance left the pool.
//
// This pool is particularly useful for performing tasks on same resource
// on the file system in different goroutines.
type ExclusivePool struct {
lock sync.Mutex
// pool maintains locks for each instance in the pool.
pool map[string]*sync.Mutex
// count maintains the number of times an instance with same identity checks in
// to the pool, and should be reduced to 0 (removed from map) by checking out
// with same number of times.
// The purpose of count is to delete lock when count down to 0 and recycle memory
// from map object.
count map[string]int
}
// NewExclusivePool initializes and returns a new ExclusivePool object.
func NewExclusivePool() *ExclusivePool {
return &ExclusivePool{
pool: make(map[string]*sync.Mutex),
count: make(map[string]int),
}
}
// CheckIn checks in an instance to the pool and hangs while instance
// with same identity is using the lock.
func (p *ExclusivePool) CheckIn(identity string) {
p.lock.Lock()
lock, has := p.pool[identity]
if !has {
lock = &sync.Mutex{}
p.pool[identity] = lock
}
p.count[identity]++
p.lock.Unlock()
lock.Lock()
}
// CheckOut checks out an instance from the pool and releases the lock
// to let other instances with same identity to grab the lock.
func (p *ExclusivePool) CheckOut(identity string) {
p.lock.Lock()
defer p.lock.Unlock()
p.pool[identity].Unlock()
if p.count[identity] == 1 {
delete(p.pool, identity)
delete(p.count, identity)
} else {
p.count[identity]--
}
}
+10 -3
View File
@@ -21,6 +21,7 @@ import (
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/globallock"
"code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/process"
@@ -334,9 +335,15 @@ func handler(items ...string) []string {
} }
func testPR(id int64) { func testPR(id int64) {
pullWorkingPool.CheckIn(fmt.Sprint(id)) ctx := graceful.GetManager().HammerContext()
defer pullWorkingPool.CheckOut(fmt.Sprint(id)) releaser, err := globallock.Lock(ctx, getPullWorkingLockKey(id))
ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("Test PR[%d] from patch checking queue", id)) if err != nil {
log.Error("lock.Lock(): %v", err)
return
}
defer releaser()
ctx, _, finished := process.GetManager().AddContext(ctx, fmt.Sprintf("Test PR[%d] from patch checking queue", id))
defer finished() defer finished()
pr, err := issues_model.GetPullRequestByID(ctx, id) pr, err := issues_model.GetPullRequestByID(ctx, id)
+18 -7
View File
@@ -23,6 +23,7 @@ import (
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/globallock"
"code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/references" "code.gitea.io/gitea/modules/references"
@@ -169,9 +170,6 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
return fmt.Errorf("unable to load head repo: %w", err) return fmt.Errorf("unable to load head repo: %w", err)
} }
pullWorkingPool.CheckIn(fmt.Sprint(pr.ID))
defer pullWorkingPool.CheckOut(fmt.Sprint(pr.ID))
prUnit, err := pr.BaseRepo.GetUnit(ctx, unit.TypePullRequests) prUnit, err := pr.BaseRepo.GetUnit(ctx, unit.TypePullRequests)
if err != nil { if err != nil {
log.Error("pr.BaseRepo.GetUnit(unit.TypePullRequests): %v", err) log.Error("pr.BaseRepo.GetUnit(unit.TypePullRequests): %v", err)
@@ -184,11 +182,18 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
return models.ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: mergeStyle} return models.ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: mergeStyle}
} }
releaser, err := globallock.Lock(ctx, getPullWorkingLockKey(pr.ID))
if err != nil {
log.Error("lock.Lock(): %v", err)
return fmt.Errorf("lock.Lock: %w", err)
}
defer releaser()
defer func() { defer func() {
go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "") go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "")
}() }()
_, err = doMergeAndPush(ctx, pr, doer, mergeStyle, expectedHeadCommitID, message, repo_module.PushTriggerPRMergeToBase) _, err = doMergeAndPush(ctx, pr, doer, mergeStyle, expectedHeadCommitID, message, repo_module.PushTriggerPRMergeToBase)
releaser()
if err != nil { if err != nil {
return err return err
} }
@@ -487,10 +492,14 @@ func CheckPullBranchProtections(ctx context.Context, pr *issues_model.PullReques
// MergedManually mark pr as merged manually // MergedManually mark pr as merged manually
func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, baseGitRepo *git.Repository, commitID string) error { func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, baseGitRepo *git.Repository, commitID string) error {
pullWorkingPool.CheckIn(fmt.Sprint(pr.ID)) releaser, err := globallock.Lock(ctx, getPullWorkingLockKey(pr.ID))
defer pullWorkingPool.CheckOut(fmt.Sprint(pr.ID)) if err != nil {
log.Error("lock.Lock(): %v", err)
return fmt.Errorf("lock.Lock: %w", err)
}
defer releaser()
if err := db.WithTx(ctx, func(ctx context.Context) error { err = db.WithTx(ctx, func(ctx context.Context) error {
if err := pr.LoadBaseRepo(ctx); err != nil { if err := pr.LoadBaseRepo(ctx); err != nil {
return err return err
} }
@@ -540,7 +549,9 @@ func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *use
return fmt.Errorf("SetMerged failed") return fmt.Errorf("SetMerged failed")
} }
return nil return nil
}); err != nil { })
releaser()
if err != nil {
return err return err
} }
+10 -5
View File
@@ -25,20 +25,21 @@ import (
"code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/globallock"
"code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository" repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/sync"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
gitea_context "code.gitea.io/gitea/services/context" gitea_context "code.gitea.io/gitea/services/context"
issue_service "code.gitea.io/gitea/services/issue" issue_service "code.gitea.io/gitea/services/issue"
notify_service "code.gitea.io/gitea/services/notify" notify_service "code.gitea.io/gitea/services/notify"
) )
// TODO: use clustered lock (unique queue? or *abuse* cache) func getPullWorkingLockKey(prID int64) string {
var pullWorkingPool = sync.NewExclusivePool() return fmt.Sprintf("pull_working_%d", prID)
}
// NewPullRequest creates new pull request with labels for repository. // NewPullRequest creates new pull request with labels for repository.
func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *issues_model.Issue, labelIDs []int64, uuids []string, pr *issues_model.PullRequest, assigneeIDs []int64) error { func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *issues_model.Issue, labelIDs []int64, uuids []string, pr *issues_model.PullRequest, assigneeIDs []int64) error {
@@ -202,8 +203,12 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *iss
// ChangeTargetBranch changes the target branch of this pull request, as the given user. // ChangeTargetBranch changes the target branch of this pull request, as the given user.
func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, targetBranch string) (err error) { func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, targetBranch string) (err error) {
pullWorkingPool.CheckIn(fmt.Sprint(pr.ID)) releaser, err := globallock.Lock(ctx, getPullWorkingLockKey(pr.ID))
defer pullWorkingPool.CheckOut(fmt.Sprint(pr.ID)) if err != nil {
log.Error("lock.Lock(): %v", err)
return fmt.Errorf("lock.Lock: %w", err)
}
defer releaser()
// Current target branch is already the same // Current target branch is already the same
if pr.BaseBranch == targetBranch { if pr.BaseBranch == targetBranch {
+7 -2
View File
@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/globallock"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/repository"
) )
@@ -25,8 +26,12 @@ func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.
return fmt.Errorf("update of agit flow pull request's head branch is unsupported") return fmt.Errorf("update of agit flow pull request's head branch is unsupported")
} }
pullWorkingPool.CheckIn(fmt.Sprint(pr.ID)) releaser, err := globallock.Lock(ctx, getPullWorkingLockKey(pr.ID))
defer pullWorkingPool.CheckOut(fmt.Sprint(pr.ID)) if err != nil {
log.Error("lock.Lock(): %v", err)
return fmt.Errorf("lock.Lock: %w", err)
}
defer releaser()
diffCount, err := GetDiverging(ctx, pr) diffCount, err := GetDiverging(ctx, pr)
if err != nil { if err != nil {
+21 -11
View File
@@ -18,16 +18,16 @@ import (
project_model "code.gitea.io/gitea/models/project" project_model "code.gitea.io/gitea/models/project"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/globallock"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository" repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/sync"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
notify_service "code.gitea.io/gitea/services/notify" notify_service "code.gitea.io/gitea/services/notify"
) )
// repoWorkingPool represents a working pool to order the parallel changes to the same repository func getRepoWorkingLockKey(repoID int64) string {
// TODO: use clustered lock (unique queue? or *abuse* cache) return fmt.Sprintf("repo_working_%d", repoID)
var repoWorkingPool = sync.NewExclusivePool() }
// TransferOwnership transfers all corresponding setting from old user to new one. // TransferOwnership transfers all corresponding setting from old user to new one.
func TransferOwnership(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository, teams []*organization.Team) error { func TransferOwnership(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository, teams []*organization.Team) error {
@@ -42,12 +42,17 @@ func TransferOwnership(ctx context.Context, doer, newOwner *user_model.User, rep
oldOwner := repo.Owner oldOwner := repo.Owner
repoWorkingPool.CheckIn(fmt.Sprint(repo.ID)) releaser, err := globallock.Lock(ctx, getRepoWorkingLockKey(repo.ID))
if err != nil {
log.Error("lock.Lock(): %v", err)
return fmt.Errorf("lock.Lock: %w", err)
}
defer releaser()
if err := transferOwnership(ctx, doer, newOwner.Name, repo); err != nil { if err := transferOwnership(ctx, doer, newOwner.Name, repo); err != nil {
repoWorkingPool.CheckOut(fmt.Sprint(repo.ID))
return err return err
} }
repoWorkingPool.CheckOut(fmt.Sprint(repo.ID)) releaser()
newRepo, err := repo_model.GetRepositoryByID(ctx, repo.ID) newRepo, err := repo_model.GetRepositoryByID(ctx, repo.ID)
if err != nil { if err != nil {
@@ -360,15 +365,20 @@ func ChangeRepositoryName(ctx context.Context, doer *user_model.User, repo *repo
oldRepoName := repo.Name oldRepoName := repo.Name
// Change repository directory name. We must lock the local copy of the // Change repository directory name. We must lock the local copy of the
// repo so that we can atomically rename the repo path and updates the // repo so that we can automatically rename the repo path and updates the
// local copy's origin accordingly. // local copy's origin accordingly.
repoWorkingPool.CheckIn(fmt.Sprint(repo.ID)) releaser, err := globallock.Lock(ctx, getRepoWorkingLockKey(repo.ID))
if err != nil {
log.Error("lock.Lock(): %v", err)
return fmt.Errorf("lock.Lock: %w", err)
}
defer releaser()
if err := changeRepositoryName(ctx, repo, newRepoName); err != nil { if err := changeRepositoryName(ctx, repo, newRepoName); err != nil {
repoWorkingPool.CheckOut(fmt.Sprint(repo.ID))
return err return err
} }
repoWorkingPool.CheckOut(fmt.Sprint(repo.ID)) releaser()
repo.Name = newRepoName repo.Name = newRepoName
notify_service.RenameRepository(ctx, doer, repo, oldRepoName) notify_service.RenameRepository(ctx, doer, repo, oldRepoName)
+15 -8
View File
@@ -18,19 +18,20 @@ import (
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/globallock"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository" repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/sync"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
asymkey_service "code.gitea.io/gitea/services/asymkey" asymkey_service "code.gitea.io/gitea/services/asymkey"
repo_service "code.gitea.io/gitea/services/repository" repo_service "code.gitea.io/gitea/services/repository"
) )
// TODO: use clustered lock (unique queue? or *abuse* cache)
var wikiWorkingPool = sync.NewExclusivePool()
const DefaultRemote = "origin" const DefaultRemote = "origin"
func getWikiWorkingLockKey(repoID int64) string {
return fmt.Sprintf("wiki_working_%d", repoID)
}
// InitWiki initializes a wiki for repository, // InitWiki initializes a wiki for repository,
// it does nothing when repository already has wiki. // it does nothing when repository already has wiki.
func InitWiki(ctx context.Context, repo *repo_model.Repository) error { func InitWiki(ctx context.Context, repo *repo_model.Repository) error {
@@ -89,8 +90,11 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
if err = validateWebPath(newWikiName); err != nil { if err = validateWebPath(newWikiName); err != nil {
return err return err
} }
wikiWorkingPool.CheckIn(fmt.Sprint(repo.ID)) releaser, err := globallock.Lock(ctx, getWikiWorkingLockKey(repo.ID))
defer wikiWorkingPool.CheckOut(fmt.Sprint(repo.ID)) if err != nil {
return err
}
defer releaser()
if err = InitWiki(ctx, repo); err != nil { if err = InitWiki(ctx, repo); err != nil {
return fmt.Errorf("InitWiki: %w", err) return fmt.Errorf("InitWiki: %w", err)
@@ -250,8 +254,11 @@ func DeleteWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
return err return err
} }
wikiWorkingPool.CheckIn(fmt.Sprint(repo.ID)) releaser, err := globallock.Lock(ctx, getWikiWorkingLockKey(repo.ID))
defer wikiWorkingPool.CheckOut(fmt.Sprint(repo.ID)) if err != nil {
return err
}
defer releaser()
if err = InitWiki(ctx, repo); err != nil { if err = InitWiki(ctx, repo); err != nil {
return fmt.Errorf("InitWiki: %w", err) return fmt.Errorf("InitWiki: %w", err)