lessor_test.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. // Copyright 2015 The etcd Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package lease
  15. import (
  16. "fmt"
  17. "io/ioutil"
  18. "os"
  19. "path/filepath"
  20. "reflect"
  21. "sort"
  22. "sync"
  23. "testing"
  24. "time"
  25. "github.com/coreos/etcd/mvcc/backend"
  26. )
  27. const (
  28. minLeaseTTL = int64(5)
  29. minLeaseTTLDuration = time.Duration(minLeaseTTL) * time.Second
  30. )
  31. // TestLessorGrant ensures Lessor can grant wanted lease.
  32. // The granted lease should have a unique ID with a term
  33. // that is greater than minLeaseTTL.
  34. func TestLessorGrant(t *testing.T) {
  35. dir, be := NewTestBackend(t)
  36. defer os.RemoveAll(dir)
  37. defer be.Close()
  38. le := newLessor(be, minLeaseTTL)
  39. le.Promote(0)
  40. l, err := le.Grant(1, 1)
  41. if err != nil {
  42. t.Fatalf("could not grant lease 1 (%v)", err)
  43. }
  44. gl := le.Lookup(l.ID)
  45. if !reflect.DeepEqual(gl, l) {
  46. t.Errorf("lease = %v, want %v", gl, l)
  47. }
  48. if l.Remaining() < minLeaseTTLDuration-time.Second {
  49. t.Errorf("term = %v, want at least %v", l.Remaining(), minLeaseTTLDuration-time.Second)
  50. }
  51. nl, err := le.Grant(1, 1)
  52. if err == nil {
  53. t.Errorf("allocated the same lease")
  54. }
  55. nl, err = le.Grant(2, 1)
  56. if err != nil {
  57. t.Errorf("could not grant lease 2 (%v)", err)
  58. }
  59. if nl.ID == l.ID {
  60. t.Errorf("new lease.id = %x, want != %x", nl.ID, l.ID)
  61. }
  62. be.BatchTx().Lock()
  63. _, vs := be.BatchTx().UnsafeRange(leaseBucketName, int64ToBytes(int64(l.ID)), nil, 0)
  64. if len(vs) != 1 {
  65. t.Errorf("len(vs) = %d, want 1", len(vs))
  66. }
  67. be.BatchTx().Unlock()
  68. }
  69. // TestLeaseConcurrentKeys ensures Lease.Keys method calls are guarded
  70. // from concurrent map writes on 'itemSet'.
  71. func TestLeaseConcurrentKeys(t *testing.T) {
  72. dir, be := NewTestBackend(t)
  73. defer os.RemoveAll(dir)
  74. defer be.Close()
  75. fd := &fakeDeleter{}
  76. le := newLessor(be, minLeaseTTL)
  77. le.SetRangeDeleter(fd)
  78. // grant a lease with long term (100 seconds) to
  79. // avoid early termination during the test.
  80. l, err := le.Grant(1, 100)
  81. if err != nil {
  82. t.Fatalf("could not grant lease for 100s ttl (%v)", err)
  83. }
  84. itemn := 10
  85. items := make([]LeaseItem, itemn)
  86. for i := 0; i < itemn; i++ {
  87. items[i] = LeaseItem{Key: fmt.Sprintf("foo%d", i)}
  88. }
  89. if err = le.Attach(l.ID, items); err != nil {
  90. t.Fatalf("failed to attach items to the lease: %v", err)
  91. }
  92. donec := make(chan struct{})
  93. go func() {
  94. le.Detach(l.ID, items)
  95. close(donec)
  96. }()
  97. var wg sync.WaitGroup
  98. wg.Add(itemn)
  99. for i := 0; i < itemn; i++ {
  100. go func() {
  101. defer wg.Done()
  102. l.Keys()
  103. }()
  104. }
  105. <-donec
  106. wg.Wait()
  107. }
  108. // TestLessorRevoke ensures Lessor can revoke a lease.
  109. // The items in the revoked lease should be removed from
  110. // the backend.
  111. // The revoked lease cannot be got from Lessor again.
  112. func TestLessorRevoke(t *testing.T) {
  113. dir, be := NewTestBackend(t)
  114. defer os.RemoveAll(dir)
  115. defer be.Close()
  116. fd := &fakeDeleter{}
  117. le := newLessor(be, minLeaseTTL)
  118. le.SetRangeDeleter(fd)
  119. // grant a lease with long term (100 seconds) to
  120. // avoid early termination during the test.
  121. l, err := le.Grant(1, 100)
  122. if err != nil {
  123. t.Fatalf("could not grant lease for 100s ttl (%v)", err)
  124. }
  125. items := []LeaseItem{
  126. {"foo"},
  127. {"bar"},
  128. }
  129. if err = le.Attach(l.ID, items); err != nil {
  130. t.Fatalf("failed to attach items to the lease: %v", err)
  131. }
  132. if err = le.Revoke(l.ID); err != nil {
  133. t.Fatal("failed to revoke lease:", err)
  134. }
  135. if le.Lookup(l.ID) != nil {
  136. t.Errorf("got revoked lease %x", l.ID)
  137. }
  138. wdeleted := []string{"bar_", "foo_"}
  139. sort.Sort(sort.StringSlice(fd.deleted))
  140. if !reflect.DeepEqual(fd.deleted, wdeleted) {
  141. t.Errorf("deleted= %v, want %v", fd.deleted, wdeleted)
  142. }
  143. be.BatchTx().Lock()
  144. _, vs := be.BatchTx().UnsafeRange(leaseBucketName, int64ToBytes(int64(l.ID)), nil, 0)
  145. if len(vs) != 0 {
  146. t.Errorf("len(vs) = %d, want 0", len(vs))
  147. }
  148. be.BatchTx().Unlock()
  149. }
  150. // TestLessorRenew ensures Lessor can renew an existing lease.
  151. func TestLessorRenew(t *testing.T) {
  152. dir, be := NewTestBackend(t)
  153. defer be.Close()
  154. defer os.RemoveAll(dir)
  155. le := newLessor(be, minLeaseTTL)
  156. le.Promote(0)
  157. l, err := le.Grant(1, minLeaseTTL)
  158. if err != nil {
  159. t.Fatalf("failed to grant lease (%v)", err)
  160. }
  161. // manually change the ttl field
  162. le.mu.Lock()
  163. l.ttl = 10
  164. le.mu.Unlock()
  165. ttl, err := le.Renew(l.ID)
  166. if err != nil {
  167. t.Fatalf("failed to renew lease (%v)", err)
  168. }
  169. if ttl != l.ttl {
  170. t.Errorf("ttl = %d, want %d", ttl, l.ttl)
  171. }
  172. l = le.Lookup(l.ID)
  173. if l.Remaining() < 9*time.Second {
  174. t.Errorf("failed to renew the lease")
  175. }
  176. }
  177. func TestLessorDetach(t *testing.T) {
  178. dir, be := NewTestBackend(t)
  179. defer os.RemoveAll(dir)
  180. defer be.Close()
  181. fd := &fakeDeleter{}
  182. le := newLessor(be, minLeaseTTL)
  183. le.SetRangeDeleter(fd)
  184. // grant a lease with long term (100 seconds) to
  185. // avoid early termination during the test.
  186. l, err := le.Grant(1, 100)
  187. if err != nil {
  188. t.Fatalf("could not grant lease for 100s ttl (%v)", err)
  189. }
  190. items := []LeaseItem{
  191. {"foo"},
  192. {"bar"},
  193. }
  194. if err := le.Attach(l.ID, items); err != nil {
  195. t.Fatalf("failed to attach items to the lease: %v", err)
  196. }
  197. if err := le.Detach(l.ID, items[0:1]); err != nil {
  198. t.Fatalf("failed to de-attach items to the lease: %v", err)
  199. }
  200. l = le.Lookup(l.ID)
  201. if len(l.itemSet) != 1 {
  202. t.Fatalf("len(l.itemSet) = %d, failed to de-attach items", len(l.itemSet))
  203. }
  204. if _, ok := l.itemSet[LeaseItem{"bar"}]; !ok {
  205. t.Fatalf("de-attached wrong item, want %q exists", "bar")
  206. }
  207. }
  208. // TestLessorRecover ensures Lessor recovers leases from
  209. // persist backend.
  210. func TestLessorRecover(t *testing.T) {
  211. dir, be := NewTestBackend(t)
  212. defer os.RemoveAll(dir)
  213. defer be.Close()
  214. le := newLessor(be, minLeaseTTL)
  215. l1, err1 := le.Grant(1, 10)
  216. l2, err2 := le.Grant(2, 20)
  217. if err1 != nil || err2 != nil {
  218. t.Fatalf("could not grant initial leases (%v, %v)", err1, err2)
  219. }
  220. // Create a new lessor with the same backend
  221. nle := newLessor(be, minLeaseTTL)
  222. nl1 := nle.Lookup(l1.ID)
  223. if nl1 == nil || nl1.ttl != l1.ttl {
  224. t.Errorf("nl1 = %v, want nl1.ttl= %d", nl1.ttl, l1.ttl)
  225. }
  226. nl2 := nle.Lookup(l2.ID)
  227. if nl2 == nil || nl2.ttl != l2.ttl {
  228. t.Errorf("nl2 = %v, want nl2.ttl= %d", nl2.ttl, l2.ttl)
  229. }
  230. }
  231. func TestLessorExpire(t *testing.T) {
  232. dir, be := NewTestBackend(t)
  233. defer os.RemoveAll(dir)
  234. defer be.Close()
  235. testMinTTL := int64(1)
  236. le := newLessor(be, testMinTTL)
  237. defer le.Stop()
  238. le.Promote(1 * time.Second)
  239. l, err := le.Grant(1, testMinTTL)
  240. if err != nil {
  241. t.Fatalf("failed to create lease: %v", err)
  242. }
  243. select {
  244. case el := <-le.ExpiredLeasesC():
  245. if el[0].ID != l.ID {
  246. t.Fatalf("expired id = %x, want %x", el[0].ID, l.ID)
  247. }
  248. case <-time.After(10 * time.Second):
  249. t.Fatalf("failed to receive expired lease")
  250. }
  251. donec := make(chan struct{})
  252. go func() {
  253. // expired lease cannot be renewed
  254. if _, err := le.Renew(l.ID); err != ErrLeaseNotFound {
  255. t.Fatalf("unexpected renew")
  256. }
  257. donec <- struct{}{}
  258. }()
  259. select {
  260. case <-donec:
  261. t.Fatalf("renew finished before lease revocation")
  262. case <-time.After(50 * time.Millisecond):
  263. }
  264. // expired lease can be revoked
  265. if err := le.Revoke(l.ID); err != nil {
  266. t.Fatalf("failed to revoke expired lease: %v", err)
  267. }
  268. select {
  269. case <-donec:
  270. case <-time.After(10 * time.Second):
  271. t.Fatalf("renew has not returned after lease revocation")
  272. }
  273. }
  274. func TestLessorExpireAndDemote(t *testing.T) {
  275. dir, be := NewTestBackend(t)
  276. defer os.RemoveAll(dir)
  277. defer be.Close()
  278. testMinTTL := int64(1)
  279. le := newLessor(be, testMinTTL)
  280. defer le.Stop()
  281. le.Promote(1 * time.Second)
  282. l, err := le.Grant(1, testMinTTL)
  283. if err != nil {
  284. t.Fatalf("failed to create lease: %v", err)
  285. }
  286. select {
  287. case el := <-le.ExpiredLeasesC():
  288. if el[0].ID != l.ID {
  289. t.Fatalf("expired id = %x, want %x", el[0].ID, l.ID)
  290. }
  291. case <-time.After(10 * time.Second):
  292. t.Fatalf("failed to receive expired lease")
  293. }
  294. donec := make(chan struct{})
  295. go func() {
  296. // expired lease cannot be renewed
  297. if _, err := le.Renew(l.ID); err != ErrNotPrimary {
  298. t.Fatalf("unexpected renew: %v", err)
  299. }
  300. donec <- struct{}{}
  301. }()
  302. select {
  303. case <-donec:
  304. t.Fatalf("renew finished before demotion")
  305. case <-time.After(50 * time.Millisecond):
  306. }
  307. // demote will cause the renew request to fail with ErrNotPrimary
  308. le.Demote()
  309. select {
  310. case <-donec:
  311. case <-time.After(10 * time.Second):
  312. t.Fatalf("renew has not returned after lessor demotion")
  313. }
  314. }
  315. type fakeDeleter struct {
  316. deleted []string
  317. }
  318. func (fd *fakeDeleter) TxnBegin() int64 {
  319. return 0
  320. }
  321. func (fd *fakeDeleter) TxnEnd(txnID int64) error {
  322. return nil
  323. }
  324. func (fd *fakeDeleter) TxnDeleteRange(tid int64, key, end []byte) (int64, int64, error) {
  325. fd.deleted = append(fd.deleted, string(key)+"_"+string(end))
  326. return 0, 0, nil
  327. }
  328. func NewTestBackend(t *testing.T) (string, backend.Backend) {
  329. tmpPath, err := ioutil.TempDir("", "lease")
  330. if err != nil {
  331. t.Fatalf("failed to create tmpdir (%v)", err)
  332. }
  333. return tmpPath, backend.New(filepath.Join(tmpPath, "be"), time.Second, 10000)
  334. }