You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

442 lines
12 KiB

Optimize read locker cleanup (#14200) When objects hold a lot of read locks cleanup time grows exponentially. ``` BEFORE: Unable to complete tests. AFTER: === RUN Test_localLocker_expireOldLocksExpire/100-locks/1-read local-locker_test.go:298: Scan Took: 0s. Left: 100/100 local-locker_test.go:317: Expire 50% took: 0s. Left: 44/44 local-locker_test.go:331: Expire rest took: 0s. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/100-locks/100-read local-locker_test.go:298: Scan Took: 0s. Left: 10000/100 local-locker_test.go:317: Expire 50% took: 1ms. Left: 5000/100 local-locker_test.go:331: Expire rest took: 1ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/100-locks/1000-read local-locker_test.go:298: Scan Took: 2ms. Left: 100000/100 local-locker_test.go:317: Expire 50% took: 55ms. Left: 50038/100 local-locker_test.go:331: Expire rest took: 29ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/10000-locks/1-read local-locker_test.go:298: Scan Took: 1ms. Left: 10000/10000 local-locker_test.go:317: Expire 50% took: 2ms. Left: 5019/5019 local-locker_test.go:331: Expire rest took: 2ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/10000-locks/100-read local-locker_test.go:298: Scan Took: 23ms. Left: 1000000/10000 local-locker_test.go:317: Expire 50% took: 160ms. Left: 499798/10000 local-locker_test.go:331: Expire rest took: 138ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/10000-locks/1000-read local-locker_test.go:298: Scan Took: 200ms. Left: 10000000/10000 local-locker_test.go:317: Expire 50% took: 5.888s. Left: 5000196/10000 local-locker_test.go:331: Expire rest took: 3.417s. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/1000000-locks/1-read local-locker_test.go:298: Scan Took: 133ms. Left: 1000000/1000000 local-locker_test.go:317: Expire 50% took: 348ms. Left: 500255/500255 local-locker_test.go:331: Expire rest took: 307ms. Left: 0/0 ```
4 years ago
Optimize read locker cleanup (#14200) When objects hold a lot of read locks cleanup time grows exponentially. ``` BEFORE: Unable to complete tests. AFTER: === RUN Test_localLocker_expireOldLocksExpire/100-locks/1-read local-locker_test.go:298: Scan Took: 0s. Left: 100/100 local-locker_test.go:317: Expire 50% took: 0s. Left: 44/44 local-locker_test.go:331: Expire rest took: 0s. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/100-locks/100-read local-locker_test.go:298: Scan Took: 0s. Left: 10000/100 local-locker_test.go:317: Expire 50% took: 1ms. Left: 5000/100 local-locker_test.go:331: Expire rest took: 1ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/100-locks/1000-read local-locker_test.go:298: Scan Took: 2ms. Left: 100000/100 local-locker_test.go:317: Expire 50% took: 55ms. Left: 50038/100 local-locker_test.go:331: Expire rest took: 29ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/10000-locks/1-read local-locker_test.go:298: Scan Took: 1ms. Left: 10000/10000 local-locker_test.go:317: Expire 50% took: 2ms. Left: 5019/5019 local-locker_test.go:331: Expire rest took: 2ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/10000-locks/100-read local-locker_test.go:298: Scan Took: 23ms. Left: 1000000/10000 local-locker_test.go:317: Expire 50% took: 160ms. Left: 499798/10000 local-locker_test.go:331: Expire rest took: 138ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/10000-locks/1000-read local-locker_test.go:298: Scan Took: 200ms. Left: 10000000/10000 local-locker_test.go:317: Expire 50% took: 5.888s. Left: 5000196/10000 local-locker_test.go:331: Expire rest took: 3.417s. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/1000000-locks/1-read local-locker_test.go:298: Scan Took: 133ms. Left: 1000000/1000000 local-locker_test.go:317: Expire 50% took: 348ms. Left: 500255/500255 local-locker_test.go:331: Expire rest took: 307ms. Left: 0/0 ```
4 years ago
Optimize read locker cleanup (#14200) When objects hold a lot of read locks cleanup time grows exponentially. ``` BEFORE: Unable to complete tests. AFTER: === RUN Test_localLocker_expireOldLocksExpire/100-locks/1-read local-locker_test.go:298: Scan Took: 0s. Left: 100/100 local-locker_test.go:317: Expire 50% took: 0s. Left: 44/44 local-locker_test.go:331: Expire rest took: 0s. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/100-locks/100-read local-locker_test.go:298: Scan Took: 0s. Left: 10000/100 local-locker_test.go:317: Expire 50% took: 1ms. Left: 5000/100 local-locker_test.go:331: Expire rest took: 1ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/100-locks/1000-read local-locker_test.go:298: Scan Took: 2ms. Left: 100000/100 local-locker_test.go:317: Expire 50% took: 55ms. Left: 50038/100 local-locker_test.go:331: Expire rest took: 29ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/10000-locks/1-read local-locker_test.go:298: Scan Took: 1ms. Left: 10000/10000 local-locker_test.go:317: Expire 50% took: 2ms. Left: 5019/5019 local-locker_test.go:331: Expire rest took: 2ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/10000-locks/100-read local-locker_test.go:298: Scan Took: 23ms. Left: 1000000/10000 local-locker_test.go:317: Expire 50% took: 160ms. Left: 499798/10000 local-locker_test.go:331: Expire rest took: 138ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/10000-locks/1000-read local-locker_test.go:298: Scan Took: 200ms. Left: 10000000/10000 local-locker_test.go:317: Expire 50% took: 5.888s. Left: 5000196/10000 local-locker_test.go:331: Expire rest took: 3.417s. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/1000000-locks/1-read local-locker_test.go:298: Scan Took: 133ms. Left: 1000000/1000000 local-locker_test.go:317: Expire 50% took: 348ms. Left: 500255/500255 local-locker_test.go:331: Expire rest took: 307ms. Left: 0/0 ```
4 years ago
Optimize read locker cleanup (#14200) When objects hold a lot of read locks cleanup time grows exponentially. ``` BEFORE: Unable to complete tests. AFTER: === RUN Test_localLocker_expireOldLocksExpire/100-locks/1-read local-locker_test.go:298: Scan Took: 0s. Left: 100/100 local-locker_test.go:317: Expire 50% took: 0s. Left: 44/44 local-locker_test.go:331: Expire rest took: 0s. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/100-locks/100-read local-locker_test.go:298: Scan Took: 0s. Left: 10000/100 local-locker_test.go:317: Expire 50% took: 1ms. Left: 5000/100 local-locker_test.go:331: Expire rest took: 1ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/100-locks/1000-read local-locker_test.go:298: Scan Took: 2ms. Left: 100000/100 local-locker_test.go:317: Expire 50% took: 55ms. Left: 50038/100 local-locker_test.go:331: Expire rest took: 29ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/10000-locks/1-read local-locker_test.go:298: Scan Took: 1ms. Left: 10000/10000 local-locker_test.go:317: Expire 50% took: 2ms. Left: 5019/5019 local-locker_test.go:331: Expire rest took: 2ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/10000-locks/100-read local-locker_test.go:298: Scan Took: 23ms. Left: 1000000/10000 local-locker_test.go:317: Expire 50% took: 160ms. Left: 499798/10000 local-locker_test.go:331: Expire rest took: 138ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/10000-locks/1000-read local-locker_test.go:298: Scan Took: 200ms. Left: 10000000/10000 local-locker_test.go:317: Expire 50% took: 5.888s. Left: 5000196/10000 local-locker_test.go:331: Expire rest took: 3.417s. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/1000000-locks/1-read local-locker_test.go:298: Scan Took: 133ms. Left: 1000000/1000000 local-locker_test.go:317: Expire 50% took: 348ms. Left: 500255/500255 local-locker_test.go:331: Expire rest took: 307ms. Left: 0/0 ```
4 years ago
Optimize read locker cleanup (#14200) When objects hold a lot of read locks cleanup time grows exponentially. ``` BEFORE: Unable to complete tests. AFTER: === RUN Test_localLocker_expireOldLocksExpire/100-locks/1-read local-locker_test.go:298: Scan Took: 0s. Left: 100/100 local-locker_test.go:317: Expire 50% took: 0s. Left: 44/44 local-locker_test.go:331: Expire rest took: 0s. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/100-locks/100-read local-locker_test.go:298: Scan Took: 0s. Left: 10000/100 local-locker_test.go:317: Expire 50% took: 1ms. Left: 5000/100 local-locker_test.go:331: Expire rest took: 1ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/100-locks/1000-read local-locker_test.go:298: Scan Took: 2ms. Left: 100000/100 local-locker_test.go:317: Expire 50% took: 55ms. Left: 50038/100 local-locker_test.go:331: Expire rest took: 29ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/10000-locks/1-read local-locker_test.go:298: Scan Took: 1ms. Left: 10000/10000 local-locker_test.go:317: Expire 50% took: 2ms. Left: 5019/5019 local-locker_test.go:331: Expire rest took: 2ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/10000-locks/100-read local-locker_test.go:298: Scan Took: 23ms. Left: 1000000/10000 local-locker_test.go:317: Expire 50% took: 160ms. Left: 499798/10000 local-locker_test.go:331: Expire rest took: 138ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/10000-locks/1000-read local-locker_test.go:298: Scan Took: 200ms. Left: 10000000/10000 local-locker_test.go:317: Expire 50% took: 5.888s. Left: 5000196/10000 local-locker_test.go:331: Expire rest took: 3.417s. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/1000000-locks/1-read local-locker_test.go:298: Scan Took: 133ms. Left: 1000000/1000000 local-locker_test.go:317: Expire 50% took: 348ms. Left: 500255/500255 local-locker_test.go:331: Expire rest took: 307ms. Left: 0/0 ```
4 years ago
Optimize read locker cleanup (#14200) When objects hold a lot of read locks cleanup time grows exponentially. ``` BEFORE: Unable to complete tests. AFTER: === RUN Test_localLocker_expireOldLocksExpire/100-locks/1-read local-locker_test.go:298: Scan Took: 0s. Left: 100/100 local-locker_test.go:317: Expire 50% took: 0s. Left: 44/44 local-locker_test.go:331: Expire rest took: 0s. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/100-locks/100-read local-locker_test.go:298: Scan Took: 0s. Left: 10000/100 local-locker_test.go:317: Expire 50% took: 1ms. Left: 5000/100 local-locker_test.go:331: Expire rest took: 1ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/100-locks/1000-read local-locker_test.go:298: Scan Took: 2ms. Left: 100000/100 local-locker_test.go:317: Expire 50% took: 55ms. Left: 50038/100 local-locker_test.go:331: Expire rest took: 29ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/10000-locks/1-read local-locker_test.go:298: Scan Took: 1ms. Left: 10000/10000 local-locker_test.go:317: Expire 50% took: 2ms. Left: 5019/5019 local-locker_test.go:331: Expire rest took: 2ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/10000-locks/100-read local-locker_test.go:298: Scan Took: 23ms. Left: 1000000/10000 local-locker_test.go:317: Expire 50% took: 160ms. Left: 499798/10000 local-locker_test.go:331: Expire rest took: 138ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/10000-locks/1000-read local-locker_test.go:298: Scan Took: 200ms. Left: 10000000/10000 local-locker_test.go:317: Expire 50% took: 5.888s. Left: 5000196/10000 local-locker_test.go:331: Expire rest took: 3.417s. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/1000000-locks/1-read local-locker_test.go:298: Scan Took: 133ms. Left: 1000000/1000000 local-locker_test.go:317: Expire 50% took: 348ms. Left: 500255/500255 local-locker_test.go:331: Expire rest took: 307ms. Left: 0/0 ```
4 years ago
Optimize read locker cleanup (#14200) When objects hold a lot of read locks cleanup time grows exponentially. ``` BEFORE: Unable to complete tests. AFTER: === RUN Test_localLocker_expireOldLocksExpire/100-locks/1-read local-locker_test.go:298: Scan Took: 0s. Left: 100/100 local-locker_test.go:317: Expire 50% took: 0s. Left: 44/44 local-locker_test.go:331: Expire rest took: 0s. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/100-locks/100-read local-locker_test.go:298: Scan Took: 0s. Left: 10000/100 local-locker_test.go:317: Expire 50% took: 1ms. Left: 5000/100 local-locker_test.go:331: Expire rest took: 1ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/100-locks/1000-read local-locker_test.go:298: Scan Took: 2ms. Left: 100000/100 local-locker_test.go:317: Expire 50% took: 55ms. Left: 50038/100 local-locker_test.go:331: Expire rest took: 29ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/10000-locks/1-read local-locker_test.go:298: Scan Took: 1ms. Left: 10000/10000 local-locker_test.go:317: Expire 50% took: 2ms. Left: 5019/5019 local-locker_test.go:331: Expire rest took: 2ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/10000-locks/100-read local-locker_test.go:298: Scan Took: 23ms. Left: 1000000/10000 local-locker_test.go:317: Expire 50% took: 160ms. Left: 499798/10000 local-locker_test.go:331: Expire rest took: 138ms. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/10000-locks/1000-read local-locker_test.go:298: Scan Took: 200ms. Left: 10000000/10000 local-locker_test.go:317: Expire 50% took: 5.888s. Left: 5000196/10000 local-locker_test.go:331: Expire rest took: 3.417s. Left: 0/0 === RUN Test_localLocker_expireOldLocksExpire/1000000-locks/1-read local-locker_test.go:298: Scan Took: 133ms. Left: 1000000/1000000 local-locker_test.go:317: Expire 50% took: 348ms. Left: 500255/500255 local-locker_test.go:331: Expire rest took: 307ms. Left: 0/0 ```
4 years ago
  1. // Copyright (c) 2015-2021 MinIO, Inc.
  2. //
  3. // This file is part of MinIO Object Storage stack
  4. //
  5. // This program is free software: you can redistribute it and/or modify
  6. // it under the terms of the GNU Affero General Public License as published by
  7. // the Free Software Foundation, either version 3 of the License, or
  8. // (at your option) any later version.
  9. //
  10. // This program is distributed in the hope that it will be useful
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. // GNU Affero General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU Affero General Public License
  16. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. package cmd
  18. import (
  19. "encoding/hex"
  20. "fmt"
  21. "math/rand"
  22. "testing"
  23. "time"
  24. "github.com/google/uuid"
  25. "github.com/minio/minio/internal/dsync"
  26. )
  27. func TestLocalLockerExpire(t *testing.T) {
  28. wResources := make([]string, 1000)
  29. rResources := make([]string, 1000)
  30. quorum := 0
  31. l := newLocker()
  32. ctx := t.Context()
  33. for i := range wResources {
  34. arg := dsync.LockArgs{
  35. UID: mustGetUUID(),
  36. Resources: []string{mustGetUUID()},
  37. Source: t.Name(),
  38. Owner: "owner",
  39. Quorum: &quorum,
  40. }
  41. ok, err := l.Lock(ctx, arg)
  42. if err != nil {
  43. t.Fatal(err)
  44. }
  45. if !ok {
  46. t.Fatal("did not get write lock")
  47. }
  48. wResources[i] = arg.Resources[0]
  49. }
  50. for i := range rResources {
  51. name := mustGetUUID()
  52. arg := dsync.LockArgs{
  53. UID: mustGetUUID(),
  54. Resources: []string{name},
  55. Source: t.Name(),
  56. Owner: "owner",
  57. Quorum: &quorum,
  58. }
  59. ok, err := l.RLock(ctx, arg)
  60. if err != nil {
  61. t.Fatal(err)
  62. }
  63. if !ok {
  64. t.Fatal("did not get read lock")
  65. }
  66. // RLock twice
  67. ok, err = l.RLock(ctx, arg)
  68. if err != nil {
  69. t.Fatal(err)
  70. }
  71. if !ok {
  72. t.Fatal("did not get write lock")
  73. }
  74. rResources[i] = arg.Resources[0]
  75. }
  76. if len(l.lockMap) != len(rResources)+len(wResources) {
  77. t.Fatalf("lockmap len, got %d, want %d + %d", len(l.lockMap), len(rResources), len(wResources))
  78. }
  79. if len(l.lockUID) != len(rResources)+len(wResources) {
  80. t.Fatalf("lockUID len, got %d, want %d + %d", len(l.lockUID), len(rResources), len(wResources))
  81. }
  82. // Expire an hour from now, should keep all
  83. l.expireOldLocks(time.Hour)
  84. if len(l.lockMap) != len(rResources)+len(wResources) {
  85. t.Fatalf("lockmap len, got %d, want %d + %d", len(l.lockMap), len(rResources), len(wResources))
  86. }
  87. if len(l.lockUID) != len(rResources)+len(wResources) {
  88. t.Fatalf("lockUID len, got %d, want %d + %d", len(l.lockUID), len(rResources), len(wResources))
  89. }
  90. // Expire a minute ago.
  91. l.expireOldLocks(-time.Minute)
  92. if len(l.lockMap) != 0 {
  93. t.Fatalf("after cleanup should be empty, got %d", len(l.lockMap))
  94. }
  95. if len(l.lockUID) != 0 {
  96. t.Fatalf("lockUID len, got %d, want %d", len(l.lockUID), 0)
  97. }
  98. }
  99. func TestLocalLockerUnlock(t *testing.T) {
  100. const n = 1000
  101. const m = 5
  102. wResources := make([][m]string, n)
  103. rResources := make([]string, n)
  104. wUIDs := make([]string, n)
  105. rUIDs := make([]string, 0, n*2)
  106. l := newLocker()
  107. ctx := t.Context()
  108. quorum := 0
  109. for i := range wResources {
  110. names := [m]string{}
  111. for j := range names {
  112. names[j] = mustGetUUID()
  113. }
  114. uid := mustGetUUID()
  115. arg := dsync.LockArgs{
  116. UID: uid,
  117. Resources: names[:],
  118. Source: t.Name(),
  119. Owner: "owner",
  120. Quorum: &quorum,
  121. }
  122. ok, err := l.Lock(ctx, arg)
  123. if err != nil {
  124. t.Fatal(err)
  125. }
  126. if !ok {
  127. t.Fatal("did not get write lock")
  128. }
  129. wResources[i] = names
  130. wUIDs[i] = uid
  131. }
  132. for i := range rResources {
  133. name := mustGetUUID()
  134. uid := mustGetUUID()
  135. arg := dsync.LockArgs{
  136. UID: uid,
  137. Resources: []string{name},
  138. Source: t.Name(),
  139. Owner: "owner",
  140. Quorum: &quorum,
  141. }
  142. ok, err := l.RLock(ctx, arg)
  143. if err != nil {
  144. t.Fatal(err)
  145. }
  146. if !ok {
  147. t.Fatal("did not get write lock")
  148. }
  149. rUIDs = append(rUIDs, uid)
  150. // RLock twice, different uid
  151. uid = mustGetUUID()
  152. arg.UID = uid
  153. ok, err = l.RLock(ctx, arg)
  154. if err != nil {
  155. t.Fatal(err)
  156. }
  157. if !ok {
  158. t.Fatal("did not get write lock")
  159. }
  160. rResources[i] = name
  161. rUIDs = append(rUIDs, uid)
  162. }
  163. // Each Lock has m entries
  164. if len(l.lockMap) != len(rResources)+len(wResources)*m {
  165. t.Fatalf("lockmap len, got %d, want %d + %d", len(l.lockMap), len(rResources), len(wResources)*m)
  166. }
  167. // A UID is added for every resource
  168. if len(l.lockUID) != len(rResources)*2+len(wResources)*m {
  169. t.Fatalf("lockUID len, got %d, want %d + %d", len(l.lockUID), len(rResources)*2, len(wResources)*m)
  170. }
  171. // RUnlock once...
  172. for i, name := range rResources {
  173. arg := dsync.LockArgs{
  174. UID: rUIDs[i*2],
  175. Resources: []string{name},
  176. Source: t.Name(),
  177. Owner: "owner",
  178. Quorum: &quorum,
  179. }
  180. ok, err := l.RUnlock(ctx, arg)
  181. if err != nil {
  182. t.Fatal(err)
  183. }
  184. if !ok {
  185. t.Fatal("did not get write lock")
  186. }
  187. }
  188. // Each Lock has m entries
  189. if len(l.lockMap) != len(rResources)+len(wResources)*m {
  190. t.Fatalf("lockmap len, got %d, want %d + %d", len(l.lockMap), len(rResources), len(wResources)*m)
  191. }
  192. // A UID is added for every resource.
  193. // We removed len(rResources) read sources.
  194. if len(l.lockUID) != len(rResources)+len(wResources)*m {
  195. t.Fatalf("lockUID len, got %d, want %d + %d", len(l.lockUID), len(rResources), len(wResources)*m)
  196. }
  197. // RUnlock again, different uids
  198. for i, name := range rResources {
  199. arg := dsync.LockArgs{
  200. UID: rUIDs[i*2+1],
  201. Resources: []string{name},
  202. Source: "minio",
  203. Owner: "owner",
  204. Quorum: &quorum,
  205. }
  206. ok, err := l.RUnlock(ctx, arg)
  207. if err != nil {
  208. t.Fatal(err)
  209. }
  210. if !ok {
  211. t.Fatal("did not get write lock")
  212. }
  213. }
  214. // Each Lock has m entries
  215. if len(l.lockMap) != 0+len(wResources)*m {
  216. t.Fatalf("lockmap len, got %d, want %d + %d", len(l.lockMap), 0, len(wResources)*m)
  217. }
  218. // A UID is added for every resource.
  219. // We removed Add Rlocked entries
  220. if len(l.lockUID) != len(wResources)*m {
  221. t.Fatalf("lockUID len, got %d, want %d + %d", len(l.lockUID), 0, len(wResources)*m)
  222. }
  223. // Remove write locked
  224. for i, names := range wResources {
  225. arg := dsync.LockArgs{
  226. UID: wUIDs[i],
  227. Resources: names[:],
  228. Source: "minio",
  229. Owner: "owner",
  230. Quorum: &quorum,
  231. }
  232. ok, err := l.Unlock(ctx, arg)
  233. if err != nil {
  234. t.Fatal(err)
  235. }
  236. if !ok {
  237. t.Fatal("did not get write lock")
  238. }
  239. }
  240. // All should be gone now...
  241. // Each Lock has m entries
  242. if len(l.lockMap) != 0 {
  243. t.Fatalf("lockmap len, got %d, want %d + %d", len(l.lockMap), 0, 0)
  244. }
  245. if len(l.lockUID) != 0 {
  246. t.Fatalf("lockUID len, got %d, want %d + %d", len(l.lockUID), 0, 0)
  247. }
  248. }
  249. func Test_localLocker_expireOldLocksExpire(t *testing.T) {
  250. rng := rand.New(rand.NewSource(0))
  251. quorum := 0
  252. // Numbers of unique locks
  253. for _, locks := range []int{100, 1000, 1e6} {
  254. if testing.Short() && locks > 100 {
  255. continue
  256. }
  257. t.Run(fmt.Sprintf("%d-locks", locks), func(t *testing.T) {
  258. // Number of readers per lock...
  259. for _, readers := range []int{1, 10, 100} {
  260. if locks > 1000 && readers > 1 {
  261. continue
  262. }
  263. if testing.Short() && readers > 10 {
  264. continue
  265. }
  266. t.Run(fmt.Sprintf("%d-read", readers), func(t *testing.T) {
  267. l := newLocker()
  268. for i := 0; i < locks; i++ {
  269. var tmp [16]byte
  270. rng.Read(tmp[:])
  271. res := []string{hex.EncodeToString(tmp[:])}
  272. for i := 0; i < readers; i++ {
  273. rng.Read(tmp[:])
  274. ok, err := l.RLock(t.Context(), dsync.LockArgs{
  275. UID: uuid.NewString(),
  276. Resources: res,
  277. Source: hex.EncodeToString(tmp[:8]),
  278. Owner: hex.EncodeToString(tmp[8:]),
  279. Quorum: &quorum,
  280. })
  281. if !ok || err != nil {
  282. t.Fatal("failed:", err, ok)
  283. }
  284. }
  285. }
  286. start := time.Now()
  287. l.expireOldLocks(time.Hour)
  288. t.Logf("Scan Took: %v. Left: %d/%d", time.Since(start).Round(time.Millisecond), len(l.lockUID), len(l.lockMap))
  289. if len(l.lockMap) != locks {
  290. t.Fatalf("objects deleted, want %d != got %d", locks, len(l.lockMap))
  291. }
  292. if len(l.lockUID) != locks*readers {
  293. t.Fatalf("objects deleted, want %d != got %d", locks*readers, len(l.lockUID))
  294. }
  295. // Expire 50%
  296. expired := time.Now().Add(-time.Hour * 2)
  297. for _, v := range l.lockMap {
  298. for i := range v {
  299. if rng.Intn(2) == 0 {
  300. v[i].TimeLastRefresh = expired.UnixNano()
  301. }
  302. }
  303. }
  304. start = time.Now()
  305. l.expireOldLocks(time.Hour)
  306. t.Logf("Expire 50%% took: %v. Left: %d/%d", time.Since(start).Round(time.Millisecond), len(l.lockUID), len(l.lockMap))
  307. if len(l.lockUID) == locks*readers {
  308. t.Fatalf("objects uids all remain, unlikely")
  309. }
  310. if len(l.lockMap) == 0 {
  311. t.Fatalf("objects all deleted, 0 remains")
  312. }
  313. if len(l.lockUID) == 0 {
  314. t.Fatalf("objects uids all deleted, 0 remains")
  315. }
  316. start = time.Now()
  317. l.expireOldLocks(-time.Minute)
  318. t.Logf("Expire rest took: %v. Left: %d/%d", time.Since(start).Round(time.Millisecond), len(l.lockUID), len(l.lockMap))
  319. if len(l.lockMap) != 0 {
  320. t.Fatalf("objects not deleted, want %d != got %d", 0, len(l.lockMap))
  321. }
  322. if len(l.lockUID) != 0 {
  323. t.Fatalf("objects not deleted, want %d != got %d", 0, len(l.lockUID))
  324. }
  325. })
  326. }
  327. })
  328. }
  329. }
  330. func Test_localLocker_RUnlock(t *testing.T) {
  331. rng := rand.New(rand.NewSource(0))
  332. quorum := 0
  333. // Numbers of unique locks
  334. for _, locks := range []int{1, 100, 1000, 1e6} {
  335. if testing.Short() && locks > 100 {
  336. continue
  337. }
  338. t.Run(fmt.Sprintf("%d-locks", locks), func(t *testing.T) {
  339. // Number of readers per lock...
  340. for _, readers := range []int{1, 10, 100} {
  341. if locks > 1000 && readers > 1 {
  342. continue
  343. }
  344. if testing.Short() && readers > 10 {
  345. continue
  346. }
  347. t.Run(fmt.Sprintf("%d-read", readers), func(t *testing.T) {
  348. l := newLocker()
  349. for i := 0; i < locks; i++ {
  350. var tmp [16]byte
  351. rng.Read(tmp[:])
  352. res := []string{hex.EncodeToString(tmp[:])}
  353. for i := 0; i < readers; i++ {
  354. rng.Read(tmp[:])
  355. ok, err := l.RLock(t.Context(), dsync.LockArgs{
  356. UID: uuid.NewString(),
  357. Resources: res,
  358. Source: hex.EncodeToString(tmp[:8]),
  359. Owner: hex.EncodeToString(tmp[8:]),
  360. Quorum: &quorum,
  361. })
  362. if !ok || err != nil {
  363. t.Fatal("failed:", err, ok)
  364. }
  365. }
  366. }
  367. // Expire 50%
  368. toUnLock := make([]dsync.LockArgs, 0, locks*readers)
  369. for k, v := range l.lockMap {
  370. for _, lock := range v {
  371. if rng.Intn(2) == 0 {
  372. toUnLock = append(toUnLock, dsync.LockArgs{Resources: []string{k}, UID: lock.UID})
  373. }
  374. }
  375. }
  376. start := time.Now()
  377. for _, lock := range toUnLock {
  378. ok, err := l.ForceUnlock(t.Context(), lock)
  379. if err != nil || !ok {
  380. t.Fatal(err)
  381. }
  382. }
  383. t.Logf("Expire 50%% took: %v. Left: %d/%d", time.Since(start).Round(time.Millisecond), len(l.lockUID), len(l.lockMap))
  384. if len(l.lockUID) == locks*readers {
  385. t.Fatalf("objects uids all remain, unlikely")
  386. }
  387. if len(l.lockMap) == 0 && locks > 10 {
  388. t.Fatalf("objects all deleted, 0 remains")
  389. }
  390. if len(l.lockUID) != locks*readers-len(toUnLock) {
  391. t.Fatalf("want %d objects uids all deleted, %d remains", len(l.lockUID), locks*readers-len(toUnLock))
  392. }
  393. toUnLock = toUnLock[:0]
  394. for k, v := range l.lockMap {
  395. for _, lock := range v {
  396. toUnLock = append(toUnLock, dsync.LockArgs{Resources: []string{k}, UID: lock.UID, Owner: lock.Owner})
  397. }
  398. }
  399. start = time.Now()
  400. for _, lock := range toUnLock {
  401. ok, err := l.RUnlock(t.Context(), lock)
  402. if err != nil || !ok {
  403. t.Fatal(err)
  404. }
  405. }
  406. t.Logf("Expire rest took: %v. Left: %d/%d", time.Since(start).Round(time.Millisecond), len(l.lockUID), len(l.lockMap))
  407. if len(l.lockMap) != 0 {
  408. t.Fatalf("objects not deleted, want %d != got %d", 0, len(l.lockMap))
  409. }
  410. if len(l.lockUID) != 0 {
  411. t.Fatalf("objects not deleted, want %d != got %d", 0, len(l.lockUID))
  412. }
  413. })
  414. }
  415. })
  416. }
  417. }