[
  {
    "path": ".gitignore",
    "content": "*~\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: go\ngo_import_path: github.com/golang/groupcache\n\nos: linux\ndist: trusty\nsudo: false\n\nscript:\n  - go test ./...\n\ngo:\n  - 1.9.x\n  - 1.10.x\n  - 1.11.x\n  - master\n\ncache:\n  directories:\n    - $GOPATH/pkg\n"
  },
  {
    "path": "LICENSE",
    "content": "Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n\"License\" shall mean the terms and conditions for use, reproduction, and\ndistribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright\nowner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all other entities\nthat control, are controlled by, or are under common control with that entity.\nFor the purposes of this definition, \"control\" means (i) the power, direct or\nindirect, to cause the direction or management of such entity, whether by\ncontract or otherwise, or (ii) ownership of fifty percent (50%) or more of the\noutstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising\npermissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications, including\nbut not limited to software source code, documentation source, and configuration\nfiles.\n\n\"Object\" form shall mean any form resulting from mechanical transformation or\ntranslation of a Source form, including but not limited to compiled object code,\ngenerated documentation, and conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made\navailable under the License, as indicated by a copyright notice that is included\nin or attached to the work (an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that\nis based on (or derived from) the Work and for which the editorial revisions,\nannotations, elaborations, or other modifications represent, as a whole, an\noriginal work of authorship. For the purposes of this License, Derivative Works\nshall not include works that remain separable from, or merely link (or bind by\nname) to the interfaces of, the Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including the original version\nof the Work and any modifications or additions to that Work or Derivative Works\nthereof, that is intentionally submitted to Licensor for inclusion in the Work\nby the copyright owner or by an individual or Legal Entity authorized to submit\non behalf of the copyright owner. For the purposes of this definition,\n\"submitted\" means any form of electronic, verbal, or written communication sent\nto the Licensor or its representatives, including but not limited to\ncommunication on electronic mailing lists, source code control systems, and\nissue tracking systems that are managed by, or on behalf of, the Licensor for\nthe purpose of discussing and improving the Work, but excluding communication\nthat is conspicuously marked or otherwise designated in writing by the copyright\nowner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf\nof whom a Contribution has been received by Licensor and subsequently\nincorporated within the Work.\n\n2. Grant of Copyright License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable copyright license to reproduce, prepare Derivative Works of,\npublicly display, publicly perform, sublicense, and distribute the Work and such\nDerivative Works in Source or Object form.\n\n3. Grant of Patent License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable (except as stated in this section) patent license to make, have\nmade, use, offer to sell, sell, import, and otherwise transfer the Work, where\nsuch license applies only to those patent claims licensable by such Contributor\nthat are necessarily infringed by their Contribution(s) alone or by combination\nof their Contribution(s) with the Work to which such Contribution(s) was\nsubmitted. If You institute patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that the Work or a\nContribution incorporated within the Work constitutes direct or contributory\npatent infringement, then any patent licenses granted to You under this License\nfor that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution.\n\nYou may reproduce and distribute copies of the Work or Derivative Works thereof\nin any medium, with or without modifications, and in Source or Object form,\nprovided that You meet the following conditions:\n\nYou must give any other recipients of the Work or Derivative Works a copy of\nthis License; and\nYou must cause any modified files to carry prominent notices stating that You\nchanged the files; and\nYou must retain, in the Source form of any Derivative Works that You distribute,\nall copyright, patent, trademark, and attribution notices from the Source form\nof the Work, excluding those notices that do not pertain to any part of the\nDerivative Works; and\nIf the Work includes a \"NOTICE\" text file as part of its distribution, then any\nDerivative Works that You distribute must include a readable copy of the\nattribution notices contained within such NOTICE file, excluding those notices\nthat do not pertain to any part of the Derivative Works, in at least one of the\nfollowing places: within a NOTICE text file distributed as part of the\nDerivative Works; within the Source form or documentation, if provided along\nwith the Derivative Works; or, within a display generated by the Derivative\nWorks, if and wherever such third-party notices normally appear. The contents of\nthe NOTICE file are for informational purposes only and do not modify the\nLicense. You may add Your own attribution notices within Derivative Works that\nYou distribute, alongside or as an addendum to the NOTICE text from the Work,\nprovided that such additional attribution notices cannot be construed as\nmodifying the License.\nYou may add Your own copyright statement to Your modifications and may provide\nadditional or different license terms and conditions for use, reproduction, or\ndistribution of Your modifications, or for any such Derivative Works as a whole,\nprovided Your use, reproduction, and distribution of the Work otherwise complies\nwith the conditions stated in this License.\n\n5. Submission of Contributions.\n\nUnless You explicitly state otherwise, any Contribution intentionally submitted\nfor inclusion in the Work by You to the Licensor shall be under the terms and\nconditions of this License, without any additional terms or conditions.\nNotwithstanding the above, nothing herein shall supersede or modify the terms of\nany separate license agreement you may have executed with Licensor regarding\nsuch Contributions.\n\n6. Trademarks.\n\nThis License does not grant permission to use the trade names, trademarks,\nservice marks, or product names of the Licensor, except as required for\nreasonable and customary use in describing the origin of the Work and\nreproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty.\n\nUnless required by applicable law or agreed to in writing, Licensor provides the\nWork (and each Contributor provides its Contributions) on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,\nincluding, without limitation, any warranties or conditions of TITLE,\nNON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are\nsolely responsible for determining the appropriateness of using or\nredistributing the Work and assume any risks associated with Your exercise of\npermissions under this License.\n\n8. Limitation of Liability.\n\nIn no event and under no legal theory, whether in tort (including negligence),\ncontract, or otherwise, unless required by applicable law (such as deliberate\nand grossly negligent acts) or agreed to in writing, shall any Contributor be\nliable to You for damages, including any direct, indirect, special, incidental,\nor consequential damages of any character arising as a result of this License or\nout of the use or inability to use the Work (including but not limited to\ndamages for loss of goodwill, work stoppage, computer failure or malfunction, or\nany and all other commercial damages or losses), even if such Contributor has\nbeen advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability.\n\nWhile redistributing the Work or Derivative Works thereof, You may choose to\noffer, and charge a fee for, acceptance of support, warranty, indemnity, or\nother liability obligations and/or rights consistent with this License. However,\nin accepting such obligations, You may act only on Your own behalf and on Your\nsole responsibility, not on behalf of any other Contributor, and only if You\nagree to indemnify, defend, and hold each Contributor harmless for any liability\nincurred by, or claims asserted against, such Contributor by reason of your\naccepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\n\nTo apply the Apache License to your work, attach the following boilerplate\nnotice, with the fields enclosed by brackets \"[]\" replaced with your own\nidentifying information. (Don't include the brackets!) The text should be\nenclosed in the appropriate comment syntax for the file format. We also\nrecommend that a file or class name and description of purpose be included on\nthe same \"printed page\" as the copyright notice for easier identification within\nthird-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# groupcache\n\n## Summary\n\ngroupcache is a distributed caching and cache-filling library, intended as a\nreplacement for a pool of memcached nodes in many cases.\n\nFor API docs and examples, see http://godoc.org/github.com/golang/groupcache\n\n## Comparison to memcached\n\n### **Like memcached**, groupcache:\n\n * shards by key to select which peer is responsible for that key\n\n### **Unlike memcached**, groupcache:\n\n * does not require running a separate set of servers, thus massively\n   reducing deployment/configuration pain.  groupcache is a client\n   library as well as a server.  It connects to its own peers, forming\n   a distributed cache.\n\n * comes with a cache filling mechanism.  Whereas memcached just says\n   \"Sorry, cache miss\", often resulting in a thundering herd of\n   database (or whatever) loads from an unbounded number of clients\n   (which has resulted in several fun outages), groupcache coordinates\n   cache fills such that only one load in one process of an entire\n   replicated set of processes populates the cache, then multiplexes\n   the loaded value to all callers.\n\n * does not support versioned values.  If key \"foo\" is value \"bar\",\n   key \"foo\" must always be \"bar\".  There are neither cache expiration\n   times, nor explicit cache evictions.  Thus there is also no CAS,\n   nor Increment/Decrement.  This also means that groupcache....\n\n * ... supports automatic mirroring of super-hot items to multiple\n   processes.  This prevents memcached hot spotting where a machine's\n   CPU and/or NIC are overloaded by very popular keys/values.\n\n * is currently only available for Go.  It's very unlikely that I\n   (bradfitz@) will port the code to any other language.\n\n## Loading process\n\nIn a nutshell, a groupcache lookup of **Get(\"foo\")** looks like:\n\n(On machine #5 of a set of N machines running the same code)\n\n 1. Is the value of \"foo\" in local memory because it's super hot?  If so, use it.\n\n 2. Is the value of \"foo\" in local memory because peer #5 (the current\n    peer) is the owner of it?  If so, use it.\n\n 3. Amongst all the peers in my set of N, am I the owner of the key\n    \"foo\"?  (e.g. does it consistent hash to 5?)  If so, load it.  If\n    other callers come in, via the same process or via RPC requests\n    from peers, they block waiting for the load to finish and get the\n    same answer.  If not, RPC to the peer that's the owner and get\n    the answer.  If the RPC fails, just load it locally (still with\n    local dup suppression).\n\n## Users\n\ngroupcache is in production use by dl.google.com (its original user),\nparts of Blogger, parts of Google Code, parts of Google Fiber, parts\nof Google production monitoring systems, etc.\n\n## Presentations\n\nSee http://talks.golang.org/2013/oscon-dl.slide\n\n## Help\n\nUse the golang-nuts mailing list for any discussion or questions.\n"
  },
  {
    "path": "byteview.go",
    "content": "/*\nCopyright 2012 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage groupcache\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"io\"\n\t\"strings\"\n)\n\n// A ByteView holds an immutable view of bytes.\n// Internally it wraps either a []byte or a string,\n// but that detail is invisible to callers.\n//\n// A ByteView is meant to be used as a value type, not\n// a pointer (like a time.Time).\ntype ByteView struct {\n\t// If b is non-nil, b is used, else s is used.\n\tb []byte\n\ts string\n}\n\n// Len returns the view's length.\nfunc (v ByteView) Len() int {\n\tif v.b != nil {\n\t\treturn len(v.b)\n\t}\n\treturn len(v.s)\n}\n\n// ByteSlice returns a copy of the data as a byte slice.\nfunc (v ByteView) ByteSlice() []byte {\n\tif v.b != nil {\n\t\treturn cloneBytes(v.b)\n\t}\n\treturn []byte(v.s)\n}\n\n// String returns the data as a string, making a copy if necessary.\nfunc (v ByteView) String() string {\n\tif v.b != nil {\n\t\treturn string(v.b)\n\t}\n\treturn v.s\n}\n\n// At returns the byte at index i.\nfunc (v ByteView) At(i int) byte {\n\tif v.b != nil {\n\t\treturn v.b[i]\n\t}\n\treturn v.s[i]\n}\n\n// Slice slices the view between the provided from and to indices.\nfunc (v ByteView) Slice(from, to int) ByteView {\n\tif v.b != nil {\n\t\treturn ByteView{b: v.b[from:to]}\n\t}\n\treturn ByteView{s: v.s[from:to]}\n}\n\n// SliceFrom slices the view from the provided index until the end.\nfunc (v ByteView) SliceFrom(from int) ByteView {\n\tif v.b != nil {\n\t\treturn ByteView{b: v.b[from:]}\n\t}\n\treturn ByteView{s: v.s[from:]}\n}\n\n// Copy copies b into dest and returns the number of bytes copied.\nfunc (v ByteView) Copy(dest []byte) int {\n\tif v.b != nil {\n\t\treturn copy(dest, v.b)\n\t}\n\treturn copy(dest, v.s)\n}\n\n// Equal returns whether the bytes in b are the same as the bytes in\n// b2.\nfunc (v ByteView) Equal(b2 ByteView) bool {\n\tif b2.b == nil {\n\t\treturn v.EqualString(b2.s)\n\t}\n\treturn v.EqualBytes(b2.b)\n}\n\n// EqualString returns whether the bytes in b are the same as the bytes\n// in s.\nfunc (v ByteView) EqualString(s string) bool {\n\tif v.b == nil {\n\t\treturn v.s == s\n\t}\n\tl := v.Len()\n\tif len(s) != l {\n\t\treturn false\n\t}\n\tfor i, bi := range v.b {\n\t\tif bi != s[i] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// EqualBytes returns whether the bytes in b are the same as the bytes\n// in b2.\nfunc (v ByteView) EqualBytes(b2 []byte) bool {\n\tif v.b != nil {\n\t\treturn bytes.Equal(v.b, b2)\n\t}\n\tl := v.Len()\n\tif len(b2) != l {\n\t\treturn false\n\t}\n\tfor i, bi := range b2 {\n\t\tif bi != v.s[i] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// Reader returns an io.ReadSeeker for the bytes in v.\nfunc (v ByteView) Reader() io.ReadSeeker {\n\tif v.b != nil {\n\t\treturn bytes.NewReader(v.b)\n\t}\n\treturn strings.NewReader(v.s)\n}\n\n// ReadAt implements io.ReaderAt on the bytes in v.\nfunc (v ByteView) ReadAt(p []byte, off int64) (n int, err error) {\n\tif off < 0 {\n\t\treturn 0, errors.New(\"view: invalid offset\")\n\t}\n\tif off >= int64(v.Len()) {\n\t\treturn 0, io.EOF\n\t}\n\tn = v.SliceFrom(int(off)).Copy(p)\n\tif n < len(p) {\n\t\terr = io.EOF\n\t}\n\treturn\n}\n\n// WriteTo implements io.WriterTo on the bytes in v.\nfunc (v ByteView) WriteTo(w io.Writer) (n int64, err error) {\n\tvar m int\n\tif v.b != nil {\n\t\tm, err = w.Write(v.b)\n\t} else {\n\t\tm, err = io.WriteString(w, v.s)\n\t}\n\tif err == nil && m < v.Len() {\n\t\terr = io.ErrShortWrite\n\t}\n\tn = int64(m)\n\treturn\n}\n"
  },
  {
    "path": "byteview_test.go",
    "content": "/*\nCopyright 2012 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage groupcache\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"testing\"\n)\n\nfunc TestByteView(t *testing.T) {\n\tfor _, s := range []string{\"\", \"x\", \"yy\"} {\n\t\tfor _, v := range []ByteView{of([]byte(s)), of(s)} {\n\t\t\tname := fmt.Sprintf(\"string %q, view %+v\", s, v)\n\t\t\tif v.Len() != len(s) {\n\t\t\t\tt.Errorf(\"%s: Len = %d; want %d\", name, v.Len(), len(s))\n\t\t\t}\n\t\t\tif v.String() != s {\n\t\t\t\tt.Errorf(\"%s: String = %q; want %q\", name, v.String(), s)\n\t\t\t}\n\t\t\tvar longDest [3]byte\n\t\t\tif n := v.Copy(longDest[:]); n != len(s) {\n\t\t\t\tt.Errorf(\"%s: long Copy = %d; want %d\", name, n, len(s))\n\t\t\t}\n\t\t\tvar shortDest [1]byte\n\t\t\tif n := v.Copy(shortDest[:]); n != min(len(s), 1) {\n\t\t\t\tt.Errorf(\"%s: short Copy = %d; want %d\", name, n, min(len(s), 1))\n\t\t\t}\n\t\t\tif got, err := ioutil.ReadAll(v.Reader()); err != nil || string(got) != s {\n\t\t\t\tt.Errorf(\"%s: Reader = %q, %v; want %q\", name, got, err, s)\n\t\t\t}\n\t\t\tif got, err := ioutil.ReadAll(io.NewSectionReader(v, 0, int64(len(s)))); err != nil || string(got) != s {\n\t\t\t\tt.Errorf(\"%s: SectionReader of ReaderAt = %q, %v; want %q\", name, got, err, s)\n\t\t\t}\n\t\t\tvar dest bytes.Buffer\n\t\t\tif _, err := v.WriteTo(&dest); err != nil || !bytes.Equal(dest.Bytes(), []byte(s)) {\n\t\t\t\tt.Errorf(\"%s: WriteTo = %q, %v; want %q\", name, dest.Bytes(), err, s)\n\t\t\t}\n\t\t}\n\t}\n}\n\n// of returns a byte view of the []byte or string in x.\nfunc of(x interface{}) ByteView {\n\tif bytes, ok := x.([]byte); ok {\n\t\treturn ByteView{b: bytes}\n\t}\n\treturn ByteView{s: x.(string)}\n}\n\nfunc TestByteViewEqual(t *testing.T) {\n\ttests := []struct {\n\t\ta    interface{} // string or []byte\n\t\tb    interface{} // string or []byte\n\t\twant bool\n\t}{\n\t\t{\"x\", \"x\", true},\n\t\t{\"x\", \"y\", false},\n\t\t{\"x\", \"yy\", false},\n\t\t{[]byte(\"x\"), []byte(\"x\"), true},\n\t\t{[]byte(\"x\"), []byte(\"y\"), false},\n\t\t{[]byte(\"x\"), []byte(\"yy\"), false},\n\t\t{[]byte(\"x\"), \"x\", true},\n\t\t{[]byte(\"x\"), \"y\", false},\n\t\t{[]byte(\"x\"), \"yy\", false},\n\t\t{\"x\", []byte(\"x\"), true},\n\t\t{\"x\", []byte(\"y\"), false},\n\t\t{\"x\", []byte(\"yy\"), false},\n\t}\n\tfor i, tt := range tests {\n\t\tva := of(tt.a)\n\t\tif bytes, ok := tt.b.([]byte); ok {\n\t\t\tif got := va.EqualBytes(bytes); got != tt.want {\n\t\t\t\tt.Errorf(\"%d. EqualBytes = %v; want %v\", i, got, tt.want)\n\t\t\t}\n\t\t} else {\n\t\t\tif got := va.EqualString(tt.b.(string)); got != tt.want {\n\t\t\t\tt.Errorf(\"%d. EqualString = %v; want %v\", i, got, tt.want)\n\t\t\t}\n\t\t}\n\t\tif got := va.Equal(of(tt.b)); got != tt.want {\n\t\t\tt.Errorf(\"%d. Equal = %v; want %v\", i, got, tt.want)\n\t\t}\n\t}\n}\n\nfunc TestByteViewSlice(t *testing.T) {\n\ttests := []struct {\n\t\tin   string\n\t\tfrom int\n\t\tto   interface{} // nil to mean the end (SliceFrom); else int\n\t\twant string\n\t}{\n\t\t{\n\t\t\tin:   \"abc\",\n\t\t\tfrom: 1,\n\t\t\tto:   2,\n\t\t\twant: \"b\",\n\t\t},\n\t\t{\n\t\t\tin:   \"abc\",\n\t\t\tfrom: 1,\n\t\t\twant: \"bc\",\n\t\t},\n\t\t{\n\t\t\tin:   \"abc\",\n\t\t\tto:   2,\n\t\t\twant: \"ab\",\n\t\t},\n\t}\n\tfor i, tt := range tests {\n\t\tfor _, v := range []ByteView{of([]byte(tt.in)), of(tt.in)} {\n\t\t\tname := fmt.Sprintf(\"test %d, view %+v\", i, v)\n\t\t\tif tt.to != nil {\n\t\t\t\tv = v.Slice(tt.from, tt.to.(int))\n\t\t\t} else {\n\t\t\t\tv = v.SliceFrom(tt.from)\n\t\t\t}\n\t\t\tif v.String() != tt.want {\n\t\t\t\tt.Errorf(\"%s: got %q; want %q\", name, v.String(), tt.want)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc min(a, b int) int {\n\tif a < b {\n\t\treturn a\n\t}\n\treturn b\n}\n"
  },
  {
    "path": "consistenthash/consistenthash.go",
    "content": "/*\nCopyright 2013 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Package consistenthash provides an implementation of a ring hash.\npackage consistenthash\n\nimport (\n\t\"hash/crc32\"\n\t\"sort\"\n\t\"strconv\"\n)\n\ntype Hash func(data []byte) uint32\n\ntype Map struct {\n\thash     Hash\n\treplicas int\n\tkeys     []int // Sorted\n\thashMap  map[int]string\n}\n\nfunc New(replicas int, fn Hash) *Map {\n\tm := &Map{\n\t\treplicas: replicas,\n\t\thash:     fn,\n\t\thashMap:  make(map[int]string),\n\t}\n\tif m.hash == nil {\n\t\tm.hash = crc32.ChecksumIEEE\n\t}\n\treturn m\n}\n\n// IsEmpty returns true if there are no items available.\nfunc (m *Map) IsEmpty() bool {\n\treturn len(m.keys) == 0\n}\n\n// Add adds some keys to the hash.\nfunc (m *Map) Add(keys ...string) {\n\tfor _, key := range keys {\n\t\tfor i := 0; i < m.replicas; i++ {\n\t\t\thash := int(m.hash([]byte(strconv.Itoa(i) + key)))\n\t\t\tm.keys = append(m.keys, hash)\n\t\t\tm.hashMap[hash] = key\n\t\t}\n\t}\n\tsort.Ints(m.keys)\n}\n\n// Get gets the closest item in the hash to the provided key.\nfunc (m *Map) Get(key string) string {\n\tif m.IsEmpty() {\n\t\treturn \"\"\n\t}\n\n\thash := int(m.hash([]byte(key)))\n\n\t// Binary search for appropriate replica.\n\tidx := sort.Search(len(m.keys), func(i int) bool { return m.keys[i] >= hash })\n\n\t// Means we have cycled back to the first replica.\n\tif idx == len(m.keys) {\n\t\tidx = 0\n\t}\n\n\treturn m.hashMap[m.keys[idx]]\n}\n"
  },
  {
    "path": "consistenthash/consistenthash_test.go",
    "content": "/*\nCopyright 2013 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage consistenthash\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"testing\"\n)\n\nfunc TestHashing(t *testing.T) {\n\n\t// Override the hash function to return easier to reason about values. Assumes\n\t// the keys can be converted to an integer.\n\thash := New(3, func(key []byte) uint32 {\n\t\ti, err := strconv.Atoi(string(key))\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\treturn uint32(i)\n\t})\n\n\t// Given the above hash function, this will give replicas with \"hashes\":\n\t// 2, 4, 6, 12, 14, 16, 22, 24, 26\n\thash.Add(\"6\", \"4\", \"2\")\n\n\ttestCases := map[string]string{\n\t\t\"2\":  \"2\",\n\t\t\"11\": \"2\",\n\t\t\"23\": \"4\",\n\t\t\"27\": \"2\",\n\t}\n\n\tfor k, v := range testCases {\n\t\tif hash.Get(k) != v {\n\t\t\tt.Errorf(\"Asking for %s, should have yielded %s\", k, v)\n\t\t}\n\t}\n\n\t// Adds 8, 18, 28\n\thash.Add(\"8\")\n\n\t// 27 should now map to 8.\n\ttestCases[\"27\"] = \"8\"\n\n\tfor k, v := range testCases {\n\t\tif hash.Get(k) != v {\n\t\t\tt.Errorf(\"Asking for %s, should have yielded %s\", k, v)\n\t\t}\n\t}\n\n}\n\nfunc TestConsistency(t *testing.T) {\n\thash1 := New(1, nil)\n\thash2 := New(1, nil)\n\n\thash1.Add(\"Bill\", \"Bob\", \"Bonny\")\n\thash2.Add(\"Bob\", \"Bonny\", \"Bill\")\n\n\tif hash1.Get(\"Ben\") != hash2.Get(\"Ben\") {\n\t\tt.Errorf(\"Fetching 'Ben' from both hashes should be the same\")\n\t}\n\n\thash2.Add(\"Becky\", \"Ben\", \"Bobby\")\n\n\tif hash1.Get(\"Ben\") != hash2.Get(\"Ben\") ||\n\t\thash1.Get(\"Bob\") != hash2.Get(\"Bob\") ||\n\t\thash1.Get(\"Bonny\") != hash2.Get(\"Bonny\") {\n\t\tt.Errorf(\"Direct matches should always return the same entry\")\n\t}\n\n}\n\nfunc BenchmarkGet8(b *testing.B)   { benchmarkGet(b, 8) }\nfunc BenchmarkGet32(b *testing.B)  { benchmarkGet(b, 32) }\nfunc BenchmarkGet128(b *testing.B) { benchmarkGet(b, 128) }\nfunc BenchmarkGet512(b *testing.B) { benchmarkGet(b, 512) }\n\nfunc benchmarkGet(b *testing.B, shards int) {\n\n\thash := New(50, nil)\n\n\tvar buckets []string\n\tfor i := 0; i < shards; i++ {\n\t\tbuckets = append(buckets, fmt.Sprintf(\"shard-%d\", i))\n\t}\n\n\thash.Add(buckets...)\n\n\tb.ResetTimer()\n\n\tfor i := 0; i < b.N; i++ {\n\t\thash.Get(buckets[i&(shards-1)])\n\t}\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/golang/groupcache\n\ngo 1.20\n\nrequire github.com/golang/protobuf v1.5.4\n\nrequire google.golang.org/protobuf v1.33.0 // indirect\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=\ngoogle.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=\ngoogle.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\n"
  },
  {
    "path": "groupcache.go",
    "content": "/*\nCopyright 2012 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Package groupcache provides a data loading mechanism with caching\n// and de-duplication that works across a set of peer processes.\n//\n// Each data Get first consults its local cache, otherwise delegates\n// to the requested key's canonical owner, which then checks its cache\n// or finally gets the data.  In the common case, many concurrent\n// cache misses across a set of peers for the same key result in just\n// one cache fill.\npackage groupcache\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"math/rand\"\n\t\"strconv\"\n\t\"sync\"\n\t\"sync/atomic\"\n\n\tpb \"github.com/golang/groupcache/groupcachepb\"\n\t\"github.com/golang/groupcache/lru\"\n\t\"github.com/golang/groupcache/singleflight\"\n)\n\n// A Getter loads data for a key.\ntype Getter interface {\n\t// Get returns the value identified by key, populating dest.\n\t//\n\t// The returned data must be unversioned. That is, key must\n\t// uniquely describe the loaded data, without an implicit\n\t// current time, and without relying on cache expiration\n\t// mechanisms.\n\tGet(ctx context.Context, key string, dest Sink) error\n}\n\n// A GetterFunc implements Getter with a function.\ntype GetterFunc func(ctx context.Context, key string, dest Sink) error\n\nfunc (f GetterFunc) Get(ctx context.Context, key string, dest Sink) error {\n\treturn f(ctx, key, dest)\n}\n\nvar (\n\tmu     sync.RWMutex\n\tgroups = make(map[string]*Group)\n\n\tinitPeerServerOnce sync.Once\n\tinitPeerServer     func()\n)\n\n// GetGroup returns the named group previously created with NewGroup, or\n// nil if there's no such group.\nfunc GetGroup(name string) *Group {\n\tmu.RLock()\n\tg := groups[name]\n\tmu.RUnlock()\n\treturn g\n}\n\n// NewGroup creates a coordinated group-aware Getter from a Getter.\n//\n// The returned Getter tries (but does not guarantee) to run only one\n// Get call at once for a given key across an entire set of peer\n// processes. Concurrent callers both in the local process and in\n// other processes receive copies of the answer once the original Get\n// completes.\n//\n// The group name must be unique for each getter.\nfunc NewGroup(name string, cacheBytes int64, getter Getter) *Group {\n\treturn newGroup(name, cacheBytes, getter, nil)\n}\n\n// If peers is nil, the peerPicker is called via a sync.Once to initialize it.\nfunc newGroup(name string, cacheBytes int64, getter Getter, peers PeerPicker) *Group {\n\tif getter == nil {\n\t\tpanic(\"nil Getter\")\n\t}\n\tmu.Lock()\n\tdefer mu.Unlock()\n\tinitPeerServerOnce.Do(callInitPeerServer)\n\tif _, dup := groups[name]; dup {\n\t\tpanic(\"duplicate registration of group \" + name)\n\t}\n\tg := &Group{\n\t\tname:       name,\n\t\tgetter:     getter,\n\t\tpeers:      peers,\n\t\tcacheBytes: cacheBytes,\n\t\tloadGroup:  &singleflight.Group{},\n\t}\n\tif fn := newGroupHook; fn != nil {\n\t\tfn(g)\n\t}\n\tgroups[name] = g\n\treturn g\n}\n\n// newGroupHook, if non-nil, is called right after a new group is created.\nvar newGroupHook func(*Group)\n\n// RegisterNewGroupHook registers a hook that is run each time\n// a group is created.\nfunc RegisterNewGroupHook(fn func(*Group)) {\n\tif newGroupHook != nil {\n\t\tpanic(\"RegisterNewGroupHook called more than once\")\n\t}\n\tnewGroupHook = fn\n}\n\n// RegisterServerStart registers a hook that is run when the first\n// group is created.\nfunc RegisterServerStart(fn func()) {\n\tif initPeerServer != nil {\n\t\tpanic(\"RegisterServerStart called more than once\")\n\t}\n\tinitPeerServer = fn\n}\n\nfunc callInitPeerServer() {\n\tif initPeerServer != nil {\n\t\tinitPeerServer()\n\t}\n}\n\n// A Group is a cache namespace and associated data loaded spread over\n// a group of 1 or more machines.\ntype Group struct {\n\tname       string\n\tgetter     Getter\n\tpeersOnce  sync.Once\n\tpeers      PeerPicker\n\tcacheBytes int64 // limit for sum of mainCache and hotCache size\n\n\t// mainCache is a cache of the keys for which this process\n\t// (amongst its peers) is authoritative. That is, this cache\n\t// contains keys which consistent hash on to this process's\n\t// peer number.\n\tmainCache cache\n\n\t// hotCache contains keys/values for which this peer is not\n\t// authoritative (otherwise they would be in mainCache), but\n\t// are popular enough to warrant mirroring in this process to\n\t// avoid going over the network to fetch from a peer.  Having\n\t// a hotCache avoids network hotspotting, where a peer's\n\t// network card could become the bottleneck on a popular key.\n\t// This cache is used sparingly to maximize the total number\n\t// of key/value pairs that can be stored globally.\n\thotCache cache\n\n\t// loadGroup ensures that each key is only fetched once\n\t// (either locally or remotely), regardless of the number of\n\t// concurrent callers.\n\tloadGroup flightGroup\n\n\t_ int32 // force Stats to be 8-byte aligned on 32-bit platforms\n\n\t// Stats are statistics on the group.\n\tStats Stats\n\n\t// rand is only non-nil when testing,\n\t// to get predictable results in TestPeers.\n\trand *rand.Rand\n}\n\n// flightGroup is defined as an interface which flightgroup.Group\n// satisfies.  We define this so that we may test with an alternate\n// implementation.\ntype flightGroup interface {\n\t// Done is called when Do is done.\n\tDo(key string, fn func() (interface{}, error)) (interface{}, error)\n}\n\n// Stats are per-group statistics.\ntype Stats struct {\n\tGets           AtomicInt // any Get request, including from peers\n\tCacheHits      AtomicInt // either cache was good\n\tPeerLoads      AtomicInt // either remote load or remote cache hit (not an error)\n\tPeerErrors     AtomicInt\n\tLoads          AtomicInt // (gets - cacheHits)\n\tLoadsDeduped   AtomicInt // after singleflight\n\tLocalLoads     AtomicInt // total good local loads\n\tLocalLoadErrs  AtomicInt // total bad local loads\n\tServerRequests AtomicInt // gets that came over the network from peers\n}\n\n// Name returns the name of the group.\nfunc (g *Group) Name() string {\n\treturn g.name\n}\n\nfunc (g *Group) initPeers() {\n\tif g.peers == nil {\n\t\tg.peers = getPeers(g.name)\n\t}\n}\n\nfunc (g *Group) Get(ctx context.Context, key string, dest Sink) error {\n\tg.peersOnce.Do(g.initPeers)\n\tg.Stats.Gets.Add(1)\n\tif dest == nil {\n\t\treturn errors.New(\"groupcache: nil dest Sink\")\n\t}\n\tvalue, cacheHit := g.lookupCache(key)\n\n\tif cacheHit {\n\t\tg.Stats.CacheHits.Add(1)\n\t\treturn setSinkView(dest, value)\n\t}\n\n\t// Optimization to avoid double unmarshalling or copying: keep\n\t// track of whether the dest was already populated. One caller\n\t// (if local) will set this; the losers will not. The common\n\t// case will likely be one caller.\n\tdestPopulated := false\n\tvalue, destPopulated, err := g.load(ctx, key, dest)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif destPopulated {\n\t\treturn nil\n\t}\n\treturn setSinkView(dest, value)\n}\n\n// load loads key either by invoking the getter locally or by sending it to another machine.\nfunc (g *Group) load(ctx context.Context, key string, dest Sink) (value ByteView, destPopulated bool, err error) {\n\tg.Stats.Loads.Add(1)\n\tviewi, err := g.loadGroup.Do(key, func() (interface{}, error) {\n\t\t// Check the cache again because singleflight can only dedup calls\n\t\t// that overlap concurrently.  It's possible for 2 concurrent\n\t\t// requests to miss the cache, resulting in 2 load() calls.  An\n\t\t// unfortunate goroutine scheduling would result in this callback\n\t\t// being run twice, serially.  If we don't check the cache again,\n\t\t// cache.nbytes would be incremented below even though there will\n\t\t// be only one entry for this key.\n\t\t//\n\t\t// Consider the following serialized event ordering for two\n\t\t// goroutines in which this callback gets called twice for the\n\t\t// same key:\n\t\t// 1: Get(\"key\")\n\t\t// 2: Get(\"key\")\n\t\t// 1: lookupCache(\"key\")\n\t\t// 2: lookupCache(\"key\")\n\t\t// 1: load(\"key\")\n\t\t// 2: load(\"key\")\n\t\t// 1: loadGroup.Do(\"key\", fn)\n\t\t// 1: fn()\n\t\t// 2: loadGroup.Do(\"key\", fn)\n\t\t// 2: fn()\n\t\tif value, cacheHit := g.lookupCache(key); cacheHit {\n\t\t\tg.Stats.CacheHits.Add(1)\n\t\t\treturn value, nil\n\t\t}\n\t\tg.Stats.LoadsDeduped.Add(1)\n\t\tvar value ByteView\n\t\tvar err error\n\t\tif peer, ok := g.peers.PickPeer(key); ok {\n\t\t\tvalue, err = g.getFromPeer(ctx, peer, key)\n\t\t\tif err == nil {\n\t\t\t\tg.Stats.PeerLoads.Add(1)\n\t\t\t\treturn value, nil\n\t\t\t}\n\t\t\tg.Stats.PeerErrors.Add(1)\n\t\t\t// TODO(bradfitz): log the peer's error? keep\n\t\t\t// log of the past few for /groupcachez?  It's\n\t\t\t// probably boring (normal task movement), so not\n\t\t\t// worth logging I imagine.\n\t\t}\n\t\tvalue, err = g.getLocally(ctx, key, dest)\n\t\tif err != nil {\n\t\t\tg.Stats.LocalLoadErrs.Add(1)\n\t\t\treturn nil, err\n\t\t}\n\t\tg.Stats.LocalLoads.Add(1)\n\t\tdestPopulated = true // only one caller of load gets this return value\n\t\tg.populateCache(key, value, &g.mainCache)\n\t\treturn value, nil\n\t})\n\tif err == nil {\n\t\tvalue = viewi.(ByteView)\n\t}\n\treturn\n}\n\nfunc (g *Group) getLocally(ctx context.Context, key string, dest Sink) (ByteView, error) {\n\terr := g.getter.Get(ctx, key, dest)\n\tif err != nil {\n\t\treturn ByteView{}, err\n\t}\n\treturn dest.view()\n}\n\nfunc (g *Group) getFromPeer(ctx context.Context, peer ProtoGetter, key string) (ByteView, error) {\n\treq := &pb.GetRequest{\n\t\tGroup: &g.name,\n\t\tKey:   &key,\n\t}\n\tres := &pb.GetResponse{}\n\terr := peer.Get(ctx, req, res)\n\tif err != nil {\n\t\treturn ByteView{}, err\n\t}\n\tvalue := ByteView{b: res.Value}\n\t// TODO(bradfitz): use res.MinuteQps or something smart to\n\t// conditionally populate hotCache.  For now just do it some\n\t// percentage of the time.\n\tvar pop bool\n\tif g.rand != nil {\n\t\tpop = g.rand.Intn(10) == 0\n\t} else {\n\t\tpop = rand.Intn(10) == 0\n\t}\n\tif pop {\n\t\tg.populateCache(key, value, &g.hotCache)\n\t}\n\treturn value, nil\n}\n\nfunc (g *Group) lookupCache(key string) (value ByteView, ok bool) {\n\tif g.cacheBytes <= 0 {\n\t\treturn\n\t}\n\tvalue, ok = g.mainCache.get(key)\n\tif ok {\n\t\treturn\n\t}\n\tvalue, ok = g.hotCache.get(key)\n\treturn\n}\n\nfunc (g *Group) populateCache(key string, value ByteView, cache *cache) {\n\tif g.cacheBytes <= 0 {\n\t\treturn\n\t}\n\tcache.add(key, value)\n\n\t// Evict items from cache(s) if necessary.\n\tfor {\n\t\tmainBytes := g.mainCache.bytes()\n\t\thotBytes := g.hotCache.bytes()\n\t\tif mainBytes+hotBytes <= g.cacheBytes {\n\t\t\treturn\n\t\t}\n\n\t\t// TODO(bradfitz): this is good-enough-for-now logic.\n\t\t// It should be something based on measurements and/or\n\t\t// respecting the costs of different resources.\n\t\tvictim := &g.mainCache\n\t\tif hotBytes > mainBytes/8 {\n\t\t\tvictim = &g.hotCache\n\t\t}\n\t\tvictim.removeOldest()\n\t}\n}\n\n// CacheType represents a type of cache.\ntype CacheType int\n\nconst (\n\t// The MainCache is the cache for items that this peer is the\n\t// owner for.\n\tMainCache CacheType = iota + 1\n\n\t// The HotCache is the cache for items that seem popular\n\t// enough to replicate to this node, even though it's not the\n\t// owner.\n\tHotCache\n)\n\n// CacheStats returns stats about the provided cache within the group.\nfunc (g *Group) CacheStats(which CacheType) CacheStats {\n\tswitch which {\n\tcase MainCache:\n\t\treturn g.mainCache.stats()\n\tcase HotCache:\n\t\treturn g.hotCache.stats()\n\tdefault:\n\t\treturn CacheStats{}\n\t}\n}\n\n// cache is a wrapper around an *lru.Cache that adds synchronization,\n// makes values always be ByteView, and counts the size of all keys and\n// values.\ntype cache struct {\n\tmu         sync.RWMutex\n\tnbytes     int64 // of all keys and values\n\tlru        *lru.Cache\n\tnhit, nget int64\n\tnevict     int64 // number of evictions\n}\n\nfunc (c *cache) stats() CacheStats {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\treturn CacheStats{\n\t\tBytes:     c.nbytes,\n\t\tItems:     c.itemsLocked(),\n\t\tGets:      c.nget,\n\t\tHits:      c.nhit,\n\t\tEvictions: c.nevict,\n\t}\n}\n\nfunc (c *cache) add(key string, value ByteView) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tif c.lru == nil {\n\t\tc.lru = &lru.Cache{\n\t\t\tOnEvicted: func(key lru.Key, value interface{}) {\n\t\t\t\tval := value.(ByteView)\n\t\t\t\tc.nbytes -= int64(len(key.(string))) + int64(val.Len())\n\t\t\t\tc.nevict++\n\t\t\t},\n\t\t}\n\t}\n\tc.lru.Add(key, value)\n\tc.nbytes += int64(len(key)) + int64(value.Len())\n}\n\nfunc (c *cache) get(key string) (value ByteView, ok bool) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tc.nget++\n\tif c.lru == nil {\n\t\treturn\n\t}\n\tvi, ok := c.lru.Get(key)\n\tif !ok {\n\t\treturn\n\t}\n\tc.nhit++\n\treturn vi.(ByteView), true\n}\n\nfunc (c *cache) removeOldest() {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tif c.lru != nil {\n\t\tc.lru.RemoveOldest()\n\t}\n}\n\nfunc (c *cache) bytes() int64 {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\treturn c.nbytes\n}\n\nfunc (c *cache) items() int64 {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\treturn c.itemsLocked()\n}\n\nfunc (c *cache) itemsLocked() int64 {\n\tif c.lru == nil {\n\t\treturn 0\n\t}\n\treturn int64(c.lru.Len())\n}\n\n// An AtomicInt is an int64 to be accessed atomically.\ntype AtomicInt int64\n\n// Add atomically adds n to i.\nfunc (i *AtomicInt) Add(n int64) {\n\tatomic.AddInt64((*int64)(i), n)\n}\n\n// Get atomically gets the value of i.\nfunc (i *AtomicInt) Get() int64 {\n\treturn atomic.LoadInt64((*int64)(i))\n}\n\nfunc (i *AtomicInt) String() string {\n\treturn strconv.FormatInt(i.Get(), 10)\n}\n\n// CacheStats are returned by stats accessors on Group.\ntype CacheStats struct {\n\tBytes     int64\n\tItems     int64\n\tGets      int64\n\tHits      int64\n\tEvictions int64\n}\n"
  },
  {
    "path": "groupcache_test.go",
    "content": "/*\nCopyright 2012 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Tests for groupcache.\n\npackage groupcache\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"hash/crc32\"\n\t\"math/rand\"\n\t\"reflect\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\t\"unsafe\"\n\n\t\"github.com/golang/protobuf/proto\"\n\n\tpb \"github.com/golang/groupcache/groupcachepb\"\n\ttestpb \"github.com/golang/groupcache/testpb\"\n)\n\nvar (\n\tonce                    sync.Once\n\tstringGroup, protoGroup Getter\n\n\tstringc = make(chan string)\n\n\tdummyCtx = context.TODO()\n\n\t// cacheFills is the number of times stringGroup or\n\t// protoGroup's Getter have been called. Read using the\n\t// cacheFills function.\n\tcacheFills AtomicInt\n)\n\nconst (\n\tstringGroupName = \"string-group\"\n\tprotoGroupName  = \"proto-group\"\n\ttestMessageType = \"google3/net/groupcache/go/test_proto.TestMessage\"\n\tfromChan        = \"from-chan\"\n\tcacheSize       = 1 << 20\n)\n\nfunc testSetup() {\n\tstringGroup = NewGroup(stringGroupName, cacheSize, GetterFunc(func(_ context.Context, key string, dest Sink) error {\n\t\tif key == fromChan {\n\t\t\tkey = <-stringc\n\t\t}\n\t\tcacheFills.Add(1)\n\t\treturn dest.SetString(\"ECHO:\" + key)\n\t}))\n\n\tprotoGroup = NewGroup(protoGroupName, cacheSize, GetterFunc(func(_ context.Context, key string, dest Sink) error {\n\t\tif key == fromChan {\n\t\t\tkey = <-stringc\n\t\t}\n\t\tcacheFills.Add(1)\n\t\treturn dest.SetProto(&testpb.TestMessage{\n\t\t\tName: proto.String(\"ECHO:\" + key),\n\t\t\tCity: proto.String(\"SOME-CITY\"),\n\t\t})\n\t}))\n}\n\n// TestGetDupSuppressString tests that a Getter's Get method is only called once with two\n// outstanding callers.  This is the string variant.\nfunc TestGetDupSuppressString(t *testing.T) {\n\tonce.Do(testSetup)\n\t// Start two getters. The first should block (waiting reading\n\t// from stringc) and the second should latch on to the first\n\t// one.\n\tresc := make(chan string, 2)\n\tfor i := 0; i < 2; i++ {\n\t\tgo func() {\n\t\t\tvar s string\n\t\t\tif err := stringGroup.Get(dummyCtx, fromChan, StringSink(&s)); err != nil {\n\t\t\t\tresc <- \"ERROR:\" + err.Error()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tresc <- s\n\t\t}()\n\t}\n\n\t// Wait a bit so both goroutines get merged together via\n\t// singleflight.\n\t// TODO(bradfitz): decide whether there are any non-offensive\n\t// debug/test hooks that could be added to singleflight to\n\t// make a sleep here unnecessary.\n\ttime.Sleep(250 * time.Millisecond)\n\n\t// Unblock the first getter, which should unblock the second\n\t// as well.\n\tstringc <- \"foo\"\n\n\tfor i := 0; i < 2; i++ {\n\t\tselect {\n\t\tcase v := <-resc:\n\t\t\tif v != \"ECHO:foo\" {\n\t\t\t\tt.Errorf(\"got %q; want %q\", v, \"ECHO:foo\")\n\t\t\t}\n\t\tcase <-time.After(5 * time.Second):\n\t\t\tt.Errorf(\"timeout waiting on getter #%d of 2\", i+1)\n\t\t}\n\t}\n}\n\n// TestGetDupSuppressProto tests that a Getter's Get method is only called once with two\n// outstanding callers.  This is the proto variant.\nfunc TestGetDupSuppressProto(t *testing.T) {\n\tonce.Do(testSetup)\n\t// Start two getters. The first should block (waiting reading\n\t// from stringc) and the second should latch on to the first\n\t// one.\n\tresc := make(chan *testpb.TestMessage, 2)\n\tfor i := 0; i < 2; i++ {\n\t\tgo func() {\n\t\t\ttm := new(testpb.TestMessage)\n\t\t\tif err := protoGroup.Get(dummyCtx, fromChan, ProtoSink(tm)); err != nil {\n\t\t\t\ttm.Name = proto.String(\"ERROR:\" + err.Error())\n\t\t\t}\n\t\t\tresc <- tm\n\t\t}()\n\t}\n\n\t// Wait a bit so both goroutines get merged together via\n\t// singleflight.\n\t// TODO(bradfitz): decide whether there are any non-offensive\n\t// debug/test hooks that could be added to singleflight to\n\t// make a sleep here unnecessary.\n\ttime.Sleep(250 * time.Millisecond)\n\n\t// Unblock the first getter, which should unblock the second\n\t// as well.\n\tstringc <- \"Fluffy\"\n\twant := &testpb.TestMessage{\n\t\tName: proto.String(\"ECHO:Fluffy\"),\n\t\tCity: proto.String(\"SOME-CITY\"),\n\t}\n\tfor i := 0; i < 2; i++ {\n\t\tselect {\n\t\tcase v := <-resc:\n\t\t\tif !reflect.DeepEqual(v, want) {\n\t\t\t\tt.Errorf(\" Got: %v\\nWant: %v\", proto.CompactTextString(v), proto.CompactTextString(want))\n\t\t\t}\n\t\tcase <-time.After(5 * time.Second):\n\t\t\tt.Errorf(\"timeout waiting on getter #%d of 2\", i+1)\n\t\t}\n\t}\n}\n\nfunc countFills(f func()) int64 {\n\tfills0 := cacheFills.Get()\n\tf()\n\treturn cacheFills.Get() - fills0\n}\n\nfunc TestCaching(t *testing.T) {\n\tonce.Do(testSetup)\n\tfills := countFills(func() {\n\t\tfor i := 0; i < 10; i++ {\n\t\t\tvar s string\n\t\t\tif err := stringGroup.Get(dummyCtx, \"TestCaching-key\", StringSink(&s)); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n\tif fills != 1 {\n\t\tt.Errorf(\"expected 1 cache fill; got %d\", fills)\n\t}\n}\n\nfunc TestCacheEviction(t *testing.T) {\n\tonce.Do(testSetup)\n\ttestKey := \"TestCacheEviction-key\"\n\tgetTestKey := func() {\n\t\tvar res string\n\t\tfor i := 0; i < 10; i++ {\n\t\t\tif err := stringGroup.Get(dummyCtx, testKey, StringSink(&res)); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t}\n\t}\n\tfills := countFills(getTestKey)\n\tif fills != 1 {\n\t\tt.Fatalf(\"expected 1 cache fill; got %d\", fills)\n\t}\n\n\tg := stringGroup.(*Group)\n\tevict0 := g.mainCache.nevict\n\n\t// Trash the cache with other keys.\n\tvar bytesFlooded int64\n\t// cacheSize/len(testKey) is approximate\n\tfor bytesFlooded < cacheSize+1024 {\n\t\tvar res string\n\t\tkey := fmt.Sprintf(\"dummy-key-%d\", bytesFlooded)\n\t\tstringGroup.Get(dummyCtx, key, StringSink(&res))\n\t\tbytesFlooded += int64(len(key) + len(res))\n\t}\n\tevicts := g.mainCache.nevict - evict0\n\tif evicts <= 0 {\n\t\tt.Errorf(\"evicts = %v; want more than 0\", evicts)\n\t}\n\n\t// Test that the key is gone.\n\tfills = countFills(getTestKey)\n\tif fills != 1 {\n\t\tt.Fatalf(\"expected 1 cache fill after cache trashing; got %d\", fills)\n\t}\n}\n\ntype fakePeer struct {\n\thits int\n\tfail bool\n}\n\nfunc (p *fakePeer) Get(_ context.Context, in *pb.GetRequest, out *pb.GetResponse) error {\n\tp.hits++\n\tif p.fail {\n\t\treturn errors.New(\"simulated error from peer\")\n\t}\n\tout.Value = []byte(\"got:\" + in.GetKey())\n\treturn nil\n}\n\ntype fakePeers []ProtoGetter\n\nfunc (p fakePeers) PickPeer(key string) (peer ProtoGetter, ok bool) {\n\tif len(p) == 0 {\n\t\treturn\n\t}\n\tn := crc32.Checksum([]byte(key), crc32.IEEETable) % uint32(len(p))\n\treturn p[n], p[n] != nil\n}\n\n// TestPeers tests that peers (virtual, in-process) are hit, and how much.\nfunc TestPeers(t *testing.T) {\n\tonce.Do(testSetup)\n\tpeer0 := &fakePeer{}\n\tpeer1 := &fakePeer{}\n\tpeer2 := &fakePeer{}\n\tpeerList := fakePeers([]ProtoGetter{peer0, peer1, peer2, nil})\n\tconst cacheSize = 0 // disabled\n\tlocalHits := 0\n\tgetter := func(_ context.Context, key string, dest Sink) error {\n\t\tlocalHits++\n\t\treturn dest.SetString(\"got:\" + key)\n\t}\n\ttestGroup := newGroup(\"TestPeers-group\", cacheSize, GetterFunc(getter), peerList)\n\ttestGroup.rand = rand.New(rand.NewSource(123))\n\trun := func(name string, n int, wantSummary string) {\n\t\t// Reset counters\n\t\tlocalHits = 0\n\t\tfor _, p := range []*fakePeer{peer0, peer1, peer2} {\n\t\t\tp.hits = 0\n\t\t}\n\n\t\tfor i := 0; i < n; i++ {\n\t\t\tkey := fmt.Sprintf(\"key-%d\", i)\n\t\t\twant := \"got:\" + key\n\t\t\tvar got string\n\t\t\terr := testGroup.Get(dummyCtx, key, StringSink(&got))\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"%s: error on key %q: %v\", name, key, err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif got != want {\n\t\t\t\tt.Errorf(\"%s: for key %q, got %q; want %q\", name, key, got, want)\n\t\t\t}\n\t\t}\n\t\tsummary := func() string {\n\t\t\treturn fmt.Sprintf(\"localHits = %d, peers = %d %d %d\", localHits, peer0.hits, peer1.hits, peer2.hits)\n\t\t}\n\t\tif got := summary(); got != wantSummary {\n\t\t\tt.Errorf(\"%s: got %q; want %q\", name, got, wantSummary)\n\t\t}\n\t}\n\tresetCacheSize := func(maxBytes int64) {\n\t\tg := testGroup\n\t\tg.cacheBytes = maxBytes\n\t\tg.mainCache = cache{}\n\t\tg.hotCache = cache{}\n\t}\n\n\t// Base case; peers all up, with no problems.\n\tresetCacheSize(1 << 20)\n\trun(\"base\", 200, \"localHits = 49, peers = 51 49 51\")\n\n\t// Verify cache was hit.  All localHits are gone, and some of\n\t// the peer hits (the ones randomly selected to be maybe hot)\n\trun(\"cached_base\", 200, \"localHits = 0, peers = 49 47 48\")\n\tresetCacheSize(0)\n\n\t// With one of the peers being down.\n\t// TODO(bradfitz): on a peer number being unavailable, the\n\t// consistent hashing should maybe keep trying others to\n\t// spread the load out. Currently it fails back to local\n\t// execution if the first consistent-hash slot is unavailable.\n\tpeerList[0] = nil\n\trun(\"one_peer_down\", 200, \"localHits = 100, peers = 0 49 51\")\n\n\t// Failing peer\n\tpeerList[0] = peer0\n\tpeer0.fail = true\n\trun(\"peer0_failing\", 200, \"localHits = 100, peers = 51 49 51\")\n}\n\nfunc TestTruncatingByteSliceTarget(t *testing.T) {\n\tvar buf [100]byte\n\ts := buf[:]\n\tif err := stringGroup.Get(dummyCtx, \"short\", TruncatingByteSliceSink(&s)); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif want := \"ECHO:short\"; string(s) != want {\n\t\tt.Errorf(\"short key got %q; want %q\", s, want)\n\t}\n\n\ts = buf[:6]\n\tif err := stringGroup.Get(dummyCtx, \"truncated\", TruncatingByteSliceSink(&s)); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif want := \"ECHO:t\"; string(s) != want {\n\t\tt.Errorf(\"truncated key got %q; want %q\", s, want)\n\t}\n}\n\nfunc TestAllocatingByteSliceTarget(t *testing.T) {\n\tvar dst []byte\n\tsink := AllocatingByteSliceSink(&dst)\n\n\tinBytes := []byte(\"some bytes\")\n\tsink.SetBytes(inBytes)\n\tif want := \"some bytes\"; string(dst) != want {\n\t\tt.Errorf(\"SetBytes resulted in %q; want %q\", dst, want)\n\t}\n\tv, err := sink.view()\n\tif err != nil {\n\t\tt.Fatalf(\"view after SetBytes failed: %v\", err)\n\t}\n\tif &inBytes[0] == &dst[0] {\n\t\tt.Error(\"inBytes and dst share memory\")\n\t}\n\tif &inBytes[0] == &v.b[0] {\n\t\tt.Error(\"inBytes and view share memory\")\n\t}\n\tif &dst[0] == &v.b[0] {\n\t\tt.Error(\"dst and view share memory\")\n\t}\n}\n\n// orderedFlightGroup allows the caller to force the schedule of when\n// orig.Do will be called.  This is useful to serialize calls such\n// that singleflight cannot dedup them.\ntype orderedFlightGroup struct {\n\tmu     sync.Mutex\n\tstage1 chan bool\n\tstage2 chan bool\n\torig   flightGroup\n}\n\nfunc (g *orderedFlightGroup) Do(key string, fn func() (interface{}, error)) (interface{}, error) {\n\t<-g.stage1\n\t<-g.stage2\n\tg.mu.Lock()\n\tdefer g.mu.Unlock()\n\treturn g.orig.Do(key, fn)\n}\n\n// TestNoDedup tests invariants on the cache size when singleflight is\n// unable to dedup calls.\nfunc TestNoDedup(t *testing.T) {\n\tconst testkey = \"testkey\"\n\tconst testval = \"testval\"\n\tg := newGroup(\"testgroup\", 1024, GetterFunc(func(_ context.Context, key string, dest Sink) error {\n\t\treturn dest.SetString(testval)\n\t}), nil)\n\n\torderedGroup := &orderedFlightGroup{\n\t\tstage1: make(chan bool),\n\t\tstage2: make(chan bool),\n\t\torig:   g.loadGroup,\n\t}\n\t// Replace loadGroup with our wrapper so we can control when\n\t// loadGroup.Do is entered for each concurrent request.\n\tg.loadGroup = orderedGroup\n\n\t// Issue two idential requests concurrently.  Since the cache is\n\t// empty, it will miss.  Both will enter load(), but we will only\n\t// allow one at a time to enter singleflight.Do, so the callback\n\t// function will be called twice.\n\tresc := make(chan string, 2)\n\tfor i := 0; i < 2; i++ {\n\t\tgo func() {\n\t\t\tvar s string\n\t\t\tif err := g.Get(dummyCtx, testkey, StringSink(&s)); err != nil {\n\t\t\t\tresc <- \"ERROR:\" + err.Error()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tresc <- s\n\t\t}()\n\t}\n\n\t// Ensure both goroutines have entered the Do routine.  This implies\n\t// both concurrent requests have checked the cache, found it empty,\n\t// and called load().\n\torderedGroup.stage1 <- true\n\torderedGroup.stage1 <- true\n\torderedGroup.stage2 <- true\n\torderedGroup.stage2 <- true\n\n\tfor i := 0; i < 2; i++ {\n\t\tif s := <-resc; s != testval {\n\t\t\tt.Errorf(\"result is %s want %s\", s, testval)\n\t\t}\n\t}\n\n\tconst wantItems = 1\n\tif g.mainCache.items() != wantItems {\n\t\tt.Errorf(\"mainCache has %d items, want %d\", g.mainCache.items(), wantItems)\n\t}\n\n\t// If the singleflight callback doesn't double-check the cache again\n\t// upon entry, we would increment nbytes twice but the entry would\n\t// only be in the cache once.\n\tconst wantBytes = int64(len(testkey) + len(testval))\n\tif g.mainCache.nbytes != wantBytes {\n\t\tt.Errorf(\"cache has %d bytes, want %d\", g.mainCache.nbytes, wantBytes)\n\t}\n}\n\nfunc TestGroupStatsAlignment(t *testing.T) {\n\tvar g Group\n\toff := unsafe.Offsetof(g.Stats)\n\tif off%8 != 0 {\n\t\tt.Fatal(\"Stats structure is not 8-byte aligned.\")\n\t}\n}\n\n// TODO(bradfitz): port the Google-internal full integration test into here,\n// using HTTP requests instead of our RPC system.\n"
  },
  {
    "path": "groupcachepb/groupcache.pb.go",
    "content": "// Code generated by protoc-gen-go.\n// source: groupcache.proto\n// DO NOT EDIT!\n\npackage groupcachepb\n\nimport proto \"github.com/golang/protobuf/proto\"\nimport json \"encoding/json\"\nimport math \"math\"\n\n// Reference proto, json, and math imports to suppress error if they are not otherwise used.\nvar _ = proto.Marshal\nvar _ = &json.SyntaxError{}\nvar _ = math.Inf\n\ntype GetRequest struct {\n\tGroup            *string `protobuf:\"bytes,1,req,name=group\" json:\"group,omitempty\"`\n\tKey              *string `protobuf:\"bytes,2,req,name=key\" json:\"key,omitempty\"`\n\tXXX_unrecognized []byte  `json:\"-\"`\n}\n\nfunc (m *GetRequest) Reset()         { *m = GetRequest{} }\nfunc (m *GetRequest) String() string { return proto.CompactTextString(m) }\nfunc (*GetRequest) ProtoMessage()    {}\n\nfunc (m *GetRequest) GetGroup() string {\n\tif m != nil && m.Group != nil {\n\t\treturn *m.Group\n\t}\n\treturn \"\"\n}\n\nfunc (m *GetRequest) GetKey() string {\n\tif m != nil && m.Key != nil {\n\t\treturn *m.Key\n\t}\n\treturn \"\"\n}\n\ntype GetResponse struct {\n\tValue            []byte   `protobuf:\"bytes,1,opt,name=value\" json:\"value,omitempty\"`\n\tMinuteQps        *float64 `protobuf:\"fixed64,2,opt,name=minute_qps\" json:\"minute_qps,omitempty\"`\n\tXXX_unrecognized []byte   `json:\"-\"`\n}\n\nfunc (m *GetResponse) Reset()         { *m = GetResponse{} }\nfunc (m *GetResponse) String() string { return proto.CompactTextString(m) }\nfunc (*GetResponse) ProtoMessage()    {}\n\nfunc (m *GetResponse) GetValue() []byte {\n\tif m != nil {\n\t\treturn m.Value\n\t}\n\treturn nil\n}\n\nfunc (m *GetResponse) GetMinuteQps() float64 {\n\tif m != nil && m.MinuteQps != nil {\n\t\treturn *m.MinuteQps\n\t}\n\treturn 0\n}\n\nfunc init() {\n}\n"
  },
  {
    "path": "groupcachepb/groupcache.proto",
    "content": "/*\nCopyright 2012 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nsyntax = \"proto2\";\n\npackage groupcachepb;\n\nmessage GetRequest {\n  required string group = 1;\n  required string key = 2; // not actually required/guaranteed to be UTF-8\n}\n\nmessage GetResponse {\n  optional bytes value = 1;\n  optional double minute_qps = 2;\n}\n\nservice GroupCache {\n  rpc Get(GetRequest) returns (GetResponse) {\n  };\n}\n"
  },
  {
    "path": "http.go",
    "content": "/*\nCopyright 2013 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage groupcache\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/golang/groupcache/consistenthash\"\n\tpb \"github.com/golang/groupcache/groupcachepb\"\n\t\"github.com/golang/protobuf/proto\"\n)\n\nconst defaultBasePath = \"/_groupcache/\"\n\nconst defaultReplicas = 50\n\n// HTTPPool implements PeerPicker for a pool of HTTP peers.\ntype HTTPPool struct {\n\t// Context optionally specifies a context for the server to use when it\n\t// receives a request.\n\t// If nil, the server uses the request's context\n\tContext func(*http.Request) context.Context\n\n\t// Transport optionally specifies an http.RoundTripper for the client\n\t// to use when it makes a request.\n\t// If nil, the client uses http.DefaultTransport.\n\tTransport func(context.Context) http.RoundTripper\n\n\t// this peer's base URL, e.g. \"https://example.net:8000\"\n\tself string\n\n\t// opts specifies the options.\n\topts HTTPPoolOptions\n\n\tmu          sync.Mutex // guards peers and httpGetters\n\tpeers       *consistenthash.Map\n\thttpGetters map[string]*httpGetter // keyed by e.g. \"http://10.0.0.2:8008\"\n}\n\n// HTTPPoolOptions are the configurations of a HTTPPool.\ntype HTTPPoolOptions struct {\n\t// BasePath specifies the HTTP path that will serve groupcache requests.\n\t// If blank, it defaults to \"/_groupcache/\".\n\tBasePath string\n\n\t// Replicas specifies the number of key replicas on the consistent hash.\n\t// If blank, it defaults to 50.\n\tReplicas int\n\n\t// HashFn specifies the hash function of the consistent hash.\n\t// If blank, it defaults to crc32.ChecksumIEEE.\n\tHashFn consistenthash.Hash\n}\n\n// NewHTTPPool initializes an HTTP pool of peers, and registers itself as a PeerPicker.\n// For convenience, it also registers itself as an http.Handler with http.DefaultServeMux.\n// The self argument should be a valid base URL that points to the current server,\n// for example \"http://example.net:8000\".\nfunc NewHTTPPool(self string) *HTTPPool {\n\tp := NewHTTPPoolOpts(self, nil)\n\thttp.Handle(p.opts.BasePath, p)\n\treturn p\n}\n\nvar httpPoolMade bool\n\n// NewHTTPPoolOpts initializes an HTTP pool of peers with the given options.\n// Unlike NewHTTPPool, this function does not register the created pool as an HTTP handler.\n// The returned *HTTPPool implements http.Handler and must be registered using http.Handle.\nfunc NewHTTPPoolOpts(self string, o *HTTPPoolOptions) *HTTPPool {\n\tif httpPoolMade {\n\t\tpanic(\"groupcache: NewHTTPPool must be called only once\")\n\t}\n\thttpPoolMade = true\n\n\tp := &HTTPPool{\n\t\tself:        self,\n\t\thttpGetters: make(map[string]*httpGetter),\n\t}\n\tif o != nil {\n\t\tp.opts = *o\n\t}\n\tif p.opts.BasePath == \"\" {\n\t\tp.opts.BasePath = defaultBasePath\n\t}\n\tif p.opts.Replicas == 0 {\n\t\tp.opts.Replicas = defaultReplicas\n\t}\n\tp.peers = consistenthash.New(p.opts.Replicas, p.opts.HashFn)\n\n\tRegisterPeerPicker(func() PeerPicker { return p })\n\treturn p\n}\n\n// Set updates the pool's list of peers.\n// Each peer value should be a valid base URL,\n// for example \"http://example.net:8000\".\nfunc (p *HTTPPool) Set(peers ...string) {\n\tp.mu.Lock()\n\tdefer p.mu.Unlock()\n\tp.peers = consistenthash.New(p.opts.Replicas, p.opts.HashFn)\n\tp.peers.Add(peers...)\n\tp.httpGetters = make(map[string]*httpGetter, len(peers))\n\tfor _, peer := range peers {\n\t\tp.httpGetters[peer] = &httpGetter{transport: p.Transport, baseURL: peer + p.opts.BasePath}\n\t}\n}\n\nfunc (p *HTTPPool) PickPeer(key string) (ProtoGetter, bool) {\n\tp.mu.Lock()\n\tdefer p.mu.Unlock()\n\tif p.peers.IsEmpty() {\n\t\treturn nil, false\n\t}\n\tif peer := p.peers.Get(key); peer != p.self {\n\t\treturn p.httpGetters[peer], true\n\t}\n\treturn nil, false\n}\n\nfunc (p *HTTPPool) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\t// Parse request.\n\tif !strings.HasPrefix(r.URL.Path, p.opts.BasePath) {\n\t\tpanic(\"HTTPPool serving unexpected path: \" + r.URL.Path)\n\t}\n\tparts := strings.SplitN(r.URL.Path[len(p.opts.BasePath):], \"/\", 2)\n\tif len(parts) != 2 {\n\t\thttp.Error(w, \"bad request\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tgroupName := parts[0]\n\tkey := parts[1]\n\n\t// Fetch the value for this group/key.\n\tgroup := GetGroup(groupName)\n\tif group == nil {\n\t\thttp.Error(w, \"no such group: \"+groupName, http.StatusNotFound)\n\t\treturn\n\t}\n\tvar ctx context.Context\n\tif p.Context != nil {\n\t\tctx = p.Context(r)\n\t} else {\n\t\tctx = r.Context()\n\t}\n\n\tgroup.Stats.ServerRequests.Add(1)\n\tvar value []byte\n\terr := group.Get(ctx, key, AllocatingByteSliceSink(&value))\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Write the value to the response body as a proto message.\n\tbody, err := proto.Marshal(&pb.GetResponse{Value: value})\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\tw.Header().Set(\"Content-Type\", \"application/x-protobuf\")\n\tw.Write(body)\n}\n\ntype httpGetter struct {\n\ttransport func(context.Context) http.RoundTripper\n\tbaseURL   string\n}\n\nvar bufferPool = sync.Pool{\n\tNew: func() interface{} { return new(bytes.Buffer) },\n}\n\nfunc (h *httpGetter) Get(ctx context.Context, in *pb.GetRequest, out *pb.GetResponse) error {\n\tu := fmt.Sprintf(\n\t\t\"%v%v/%v\",\n\t\th.baseURL,\n\t\turl.QueryEscape(in.GetGroup()),\n\t\turl.QueryEscape(in.GetKey()),\n\t)\n\treq, err := http.NewRequest(\"GET\", u, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\treq = req.WithContext(ctx)\n\ttr := http.DefaultTransport\n\tif h.transport != nil {\n\t\ttr = h.transport(ctx)\n\t}\n\tres, err := tr.RoundTrip(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer res.Body.Close()\n\tif res.StatusCode != http.StatusOK {\n\t\treturn fmt.Errorf(\"server returned: %v\", res.Status)\n\t}\n\tb := bufferPool.Get().(*bytes.Buffer)\n\tb.Reset()\n\tdefer bufferPool.Put(b)\n\t_, err = io.Copy(b, res.Body)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"reading response body: %v\", err)\n\t}\n\terr = proto.Unmarshal(b.Bytes(), out)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"decoding response body: %v\", err)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "http_test.go",
    "content": "/*\nCopyright 2013 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage groupcache\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"flag\"\n\t\"log\"\n\t\"net\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar (\n\tpeerAddrs = flag.String(\"test_peer_addrs\", \"\", \"Comma-separated list of peer addresses; used by TestHTTPPool\")\n\tpeerIndex = flag.Int(\"test_peer_index\", -1, \"Index of which peer this child is; used by TestHTTPPool\")\n\tpeerChild = flag.Bool(\"test_peer_child\", false, \"True if running as a child process; used by TestHTTPPool\")\n)\n\nfunc TestHTTPPool(t *testing.T) {\n\tif *peerChild {\n\t\tbeChildForTestHTTPPool()\n\t\tos.Exit(0)\n\t}\n\n\tconst (\n\t\tnChild = 4\n\t\tnGets  = 100\n\t)\n\n\tvar childAddr []string\n\tfor i := 0; i < nChild; i++ {\n\t\tchildAddr = append(childAddr, pickFreeAddr(t))\n\t}\n\n\tvar cmds []*exec.Cmd\n\tvar wg sync.WaitGroup\n\tfor i := 0; i < nChild; i++ {\n\t\tcmd := exec.Command(os.Args[0],\n\t\t\t\"--test.run=TestHTTPPool\",\n\t\t\t\"--test_peer_child\",\n\t\t\t\"--test_peer_addrs=\"+strings.Join(childAddr, \",\"),\n\t\t\t\"--test_peer_index=\"+strconv.Itoa(i),\n\t\t)\n\t\tcmds = append(cmds, cmd)\n\t\twg.Add(1)\n\t\tif err := cmd.Start(); err != nil {\n\t\t\tt.Fatal(\"failed to start child process: \", err)\n\t\t}\n\t\tgo awaitAddrReady(t, childAddr[i], &wg)\n\t}\n\tdefer func() {\n\t\tfor i := 0; i < nChild; i++ {\n\t\t\tif cmds[i].Process != nil {\n\t\t\t\tcmds[i].Process.Kill()\n\t\t\t}\n\t\t}\n\t}()\n\twg.Wait()\n\n\t// Use a dummy self address so that we don't handle gets in-process.\n\tp := NewHTTPPool(\"should-be-ignored\")\n\tp.Set(addrToURL(childAddr)...)\n\n\t// Dummy getter function. Gets should go to children only.\n\t// The only time this process will handle a get is when the\n\t// children can't be contacted for some reason.\n\tgetter := GetterFunc(func(ctx context.Context, key string, dest Sink) error {\n\t\treturn errors.New(\"parent getter called; something's wrong\")\n\t})\n\tg := NewGroup(\"httpPoolTest\", 1<<20, getter)\n\n\tfor _, key := range testKeys(nGets) {\n\t\tvar value string\n\t\tif err := g.Get(context.TODO(), key, StringSink(&value)); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif suffix := \":\" + key; !strings.HasSuffix(value, suffix) {\n\t\t\tt.Errorf(\"Get(%q) = %q, want value ending in %q\", key, value, suffix)\n\t\t}\n\t\tt.Logf(\"Get key=%q, value=%q (peer:key)\", key, value)\n\t}\n}\n\nfunc testKeys(n int) (keys []string) {\n\tkeys = make([]string, n)\n\tfor i := range keys {\n\t\tkeys[i] = strconv.Itoa(i)\n\t}\n\treturn\n}\n\nfunc beChildForTestHTTPPool() {\n\taddrs := strings.Split(*peerAddrs, \",\")\n\n\tp := NewHTTPPool(\"http://\" + addrs[*peerIndex])\n\tp.Set(addrToURL(addrs)...)\n\n\tgetter := GetterFunc(func(ctx context.Context, key string, dest Sink) error {\n\t\tdest.SetString(strconv.Itoa(*peerIndex) + \":\" + key)\n\t\treturn nil\n\t})\n\tNewGroup(\"httpPoolTest\", 1<<20, getter)\n\n\tlog.Fatal(http.ListenAndServe(addrs[*peerIndex], p))\n}\n\n// This is racy. Another process could swoop in and steal the port between the\n// call to this function and the next listen call. Should be okay though.\n// The proper way would be to pass the l.File() as ExtraFiles to the child\n// process, and then close your copy once the child starts.\nfunc pickFreeAddr(t *testing.T) string {\n\tl, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer l.Close()\n\treturn l.Addr().String()\n}\n\nfunc addrToURL(addr []string) []string {\n\turl := make([]string, len(addr))\n\tfor i := range addr {\n\t\turl[i] = \"http://\" + addr[i]\n\t}\n\treturn url\n}\n\nfunc awaitAddrReady(t *testing.T, addr string, wg *sync.WaitGroup) {\n\tdefer wg.Done()\n\tconst max = 1 * time.Second\n\ttries := 0\n\tfor {\n\t\ttries++\n\t\tc, err := net.Dial(\"tcp\", addr)\n\t\tif err == nil {\n\t\t\tc.Close()\n\t\t\treturn\n\t\t}\n\t\tdelay := time.Duration(tries) * 25 * time.Millisecond\n\t\tif delay > max {\n\t\t\tdelay = max\n\t\t}\n\t\ttime.Sleep(delay)\n\t}\n}\n"
  },
  {
    "path": "lru/lru.go",
    "content": "/*\nCopyright 2013 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Package lru implements an LRU cache.\npackage lru\n\nimport \"container/list\"\n\n// Cache is an LRU cache. It is not safe for concurrent access.\ntype Cache struct {\n\t// MaxEntries is the maximum number of cache entries before\n\t// an item is evicted. Zero means no limit.\n\tMaxEntries int\n\n\t// OnEvicted optionally specifies a callback function to be\n\t// executed when an entry is purged from the cache.\n\tOnEvicted func(key Key, value interface{})\n\n\tll    *list.List\n\tcache map[interface{}]*list.Element\n}\n\n// A Key may be any value that is comparable. See http://golang.org/ref/spec#Comparison_operators\ntype Key interface{}\n\ntype entry struct {\n\tkey   Key\n\tvalue interface{}\n}\n\n// New creates a new Cache.\n// If maxEntries is zero, the cache has no limit and it's assumed\n// that eviction is done by the caller.\nfunc New(maxEntries int) *Cache {\n\treturn &Cache{\n\t\tMaxEntries: maxEntries,\n\t\tll:         list.New(),\n\t\tcache:      make(map[interface{}]*list.Element),\n\t}\n}\n\n// Add adds a value to the cache.\nfunc (c *Cache) Add(key Key, value interface{}) {\n\tif c.cache == nil {\n\t\tc.cache = make(map[interface{}]*list.Element)\n\t\tc.ll = list.New()\n\t}\n\tif ee, ok := c.cache[key]; ok {\n\t\tc.ll.MoveToFront(ee)\n\t\tee.Value.(*entry).value = value\n\t\treturn\n\t}\n\tele := c.ll.PushFront(&entry{key, value})\n\tc.cache[key] = ele\n\tif c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries {\n\t\tc.RemoveOldest()\n\t}\n}\n\n// Get looks up a key's value from the cache.\nfunc (c *Cache) Get(key Key) (value interface{}, ok bool) {\n\tif c.cache == nil {\n\t\treturn\n\t}\n\tif ele, hit := c.cache[key]; hit {\n\t\tc.ll.MoveToFront(ele)\n\t\treturn ele.Value.(*entry).value, true\n\t}\n\treturn\n}\n\n// Remove removes the provided key from the cache.\nfunc (c *Cache) Remove(key Key) {\n\tif c.cache == nil {\n\t\treturn\n\t}\n\tif ele, hit := c.cache[key]; hit {\n\t\tc.removeElement(ele)\n\t}\n}\n\n// RemoveOldest removes the oldest item from the cache.\nfunc (c *Cache) RemoveOldest() {\n\tif c.cache == nil {\n\t\treturn\n\t}\n\tele := c.ll.Back()\n\tif ele != nil {\n\t\tc.removeElement(ele)\n\t}\n}\n\nfunc (c *Cache) removeElement(e *list.Element) {\n\tc.ll.Remove(e)\n\tkv := e.Value.(*entry)\n\tdelete(c.cache, kv.key)\n\tif c.OnEvicted != nil {\n\t\tc.OnEvicted(kv.key, kv.value)\n\t}\n}\n\n// Len returns the number of items in the cache.\nfunc (c *Cache) Len() int {\n\tif c.cache == nil {\n\t\treturn 0\n\t}\n\treturn c.ll.Len()\n}\n\n// Clear purges all stored items from the cache.\nfunc (c *Cache) Clear() {\n\tif c.OnEvicted != nil {\n\t\tfor _, e := range c.cache {\n\t\t\tkv := e.Value.(*entry)\n\t\t\tc.OnEvicted(kv.key, kv.value)\n\t\t}\n\t}\n\tc.ll = nil\n\tc.cache = nil\n}\n"
  },
  {
    "path": "lru/lru_test.go",
    "content": "/*\nCopyright 2013 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage lru\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\ntype simpleStruct struct {\n\tint\n\tstring\n}\n\ntype complexStruct struct {\n\tint\n\tsimpleStruct\n}\n\nvar getTests = []struct {\n\tname       string\n\tkeyToAdd   interface{}\n\tkeyToGet   interface{}\n\texpectedOk bool\n}{\n\t{\"string_hit\", \"myKey\", \"myKey\", true},\n\t{\"string_miss\", \"myKey\", \"nonsense\", false},\n\t{\"simple_struct_hit\", simpleStruct{1, \"two\"}, simpleStruct{1, \"two\"}, true},\n\t{\"simple_struct_miss\", simpleStruct{1, \"two\"}, simpleStruct{0, \"noway\"}, false},\n\t{\"complex_struct_hit\", complexStruct{1, simpleStruct{2, \"three\"}},\n\t\tcomplexStruct{1, simpleStruct{2, \"three\"}}, true},\n}\n\nfunc TestGet(t *testing.T) {\n\tfor _, tt := range getTests {\n\t\tlru := New(0)\n\t\tlru.Add(tt.keyToAdd, 1234)\n\t\tval, ok := lru.Get(tt.keyToGet)\n\t\tif ok != tt.expectedOk {\n\t\t\tt.Fatalf(\"%s: cache hit = %v; want %v\", tt.name, ok, !ok)\n\t\t} else if ok && val != 1234 {\n\t\t\tt.Fatalf(\"%s expected get to return 1234 but got %v\", tt.name, val)\n\t\t}\n\t}\n}\n\nfunc TestRemove(t *testing.T) {\n\tlru := New(0)\n\tlru.Add(\"myKey\", 1234)\n\tif val, ok := lru.Get(\"myKey\"); !ok {\n\t\tt.Fatal(\"TestRemove returned no match\")\n\t} else if val != 1234 {\n\t\tt.Fatalf(\"TestRemove failed.  Expected %d, got %v\", 1234, val)\n\t}\n\n\tlru.Remove(\"myKey\")\n\tif _, ok := lru.Get(\"myKey\"); ok {\n\t\tt.Fatal(\"TestRemove returned a removed entry\")\n\t}\n}\n\nfunc TestEvict(t *testing.T) {\n\tevictedKeys := make([]Key, 0)\n\tonEvictedFun := func(key Key, value interface{}) {\n\t\tevictedKeys = append(evictedKeys, key)\n\t}\n\n\tlru := New(20)\n\tlru.OnEvicted = onEvictedFun\n\tfor i := 0; i < 22; i++ {\n\t\tlru.Add(fmt.Sprintf(\"myKey%d\", i), 1234)\n\t}\n\n\tif len(evictedKeys) != 2 {\n\t\tt.Fatalf(\"got %d evicted keys; want 2\", len(evictedKeys))\n\t}\n\tif evictedKeys[0] != Key(\"myKey0\") {\n\t\tt.Fatalf(\"got %v in first evicted key; want %s\", evictedKeys[0], \"myKey0\")\n\t}\n\tif evictedKeys[1] != Key(\"myKey1\") {\n\t\tt.Fatalf(\"got %v in second evicted key; want %s\", evictedKeys[1], \"myKey1\")\n\t}\n}\n"
  },
  {
    "path": "peers.go",
    "content": "/*\nCopyright 2012 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// peers.go defines how processes find and communicate with their peers.\n\npackage groupcache\n\nimport (\n\t\"context\"\n\n\tpb \"github.com/golang/groupcache/groupcachepb\"\n)\n\n// Context is an alias to context.Context for backwards compatibility purposes.\ntype Context = context.Context\n\n// ProtoGetter is the interface that must be implemented by a peer.\ntype ProtoGetter interface {\n\tGet(ctx context.Context, in *pb.GetRequest, out *pb.GetResponse) error\n}\n\n// PeerPicker is the interface that must be implemented to locate\n// the peer that owns a specific key.\ntype PeerPicker interface {\n\t// PickPeer returns the peer that owns the specific key\n\t// and true to indicate that a remote peer was nominated.\n\t// It returns nil, false if the key owner is the current peer.\n\tPickPeer(key string) (peer ProtoGetter, ok bool)\n}\n\n// NoPeers is an implementation of PeerPicker that never finds a peer.\ntype NoPeers struct{}\n\nfunc (NoPeers) PickPeer(key string) (peer ProtoGetter, ok bool) { return }\n\nvar (\n\tportPicker func(groupName string) PeerPicker\n)\n\n// RegisterPeerPicker registers the peer initialization function.\n// It is called once, when the first group is created.\n// Either RegisterPeerPicker or RegisterPerGroupPeerPicker should be\n// called exactly once, but not both.\nfunc RegisterPeerPicker(fn func() PeerPicker) {\n\tif portPicker != nil {\n\t\tpanic(\"RegisterPeerPicker called more than once\")\n\t}\n\tportPicker = func(_ string) PeerPicker { return fn() }\n}\n\n// RegisterPerGroupPeerPicker registers the peer initialization function,\n// which takes the groupName, to be used in choosing a PeerPicker.\n// It is called once, when the first group is created.\n// Either RegisterPeerPicker or RegisterPerGroupPeerPicker should be\n// called exactly once, but not both.\nfunc RegisterPerGroupPeerPicker(fn func(groupName string) PeerPicker) {\n\tif portPicker != nil {\n\t\tpanic(\"RegisterPeerPicker called more than once\")\n\t}\n\tportPicker = fn\n}\n\nfunc getPeers(groupName string) PeerPicker {\n\tif portPicker == nil {\n\t\treturn NoPeers{}\n\t}\n\tpk := portPicker(groupName)\n\tif pk == nil {\n\t\tpk = NoPeers{}\n\t}\n\treturn pk\n}\n"
  },
  {
    "path": "singleflight/singleflight.go",
    "content": "/*\nCopyright 2012 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Package singleflight provides a duplicate function call suppression\n// mechanism.\npackage singleflight\n\nimport \"sync\"\n\n// call is an in-flight or completed Do call\ntype call struct {\n\twg  sync.WaitGroup\n\tval interface{}\n\terr error\n}\n\n// Group represents a class of work and forms a namespace in which\n// units of work can be executed with duplicate suppression.\ntype Group struct {\n\tmu sync.Mutex       // protects m\n\tm  map[string]*call // lazily initialized\n}\n\n// Do executes and returns the results of the given function, making\n// sure that only one execution is in-flight for a given key at a\n// time. If a duplicate comes in, the duplicate caller waits for the\n// original to complete and receives the same results.\nfunc (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) {\n\tg.mu.Lock()\n\tif g.m == nil {\n\t\tg.m = make(map[string]*call)\n\t}\n\tif c, ok := g.m[key]; ok {\n\t\tg.mu.Unlock()\n\t\tc.wg.Wait()\n\t\treturn c.val, c.err\n\t}\n\tc := new(call)\n\tc.wg.Add(1)\n\tg.m[key] = c\n\tg.mu.Unlock()\n\n\tc.val, c.err = fn()\n\tc.wg.Done()\n\n\tg.mu.Lock()\n\tdelete(g.m, key)\n\tg.mu.Unlock()\n\n\treturn c.val, c.err\n}\n"
  },
  {
    "path": "singleflight/singleflight_test.go",
    "content": "/*\nCopyright 2012 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage singleflight\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestDo(t *testing.T) {\n\tvar g Group\n\tv, err := g.Do(\"key\", func() (interface{}, error) {\n\t\treturn \"bar\", nil\n\t})\n\tif got, want := fmt.Sprintf(\"%v (%T)\", v, v), \"bar (string)\"; got != want {\n\t\tt.Errorf(\"Do = %v; want %v\", got, want)\n\t}\n\tif err != nil {\n\t\tt.Errorf(\"Do error = %v\", err)\n\t}\n}\n\nfunc TestDoErr(t *testing.T) {\n\tvar g Group\n\tsomeErr := errors.New(\"some error\")\n\tv, err := g.Do(\"key\", func() (interface{}, error) {\n\t\treturn nil, someErr\n\t})\n\tif err != someErr {\n\t\tt.Errorf(\"Do error = %v; want someErr\", err)\n\t}\n\tif v != nil {\n\t\tt.Errorf(\"unexpected non-nil value %#v\", v)\n\t}\n}\n\nfunc TestDoDupSuppress(t *testing.T) {\n\tvar g Group\n\tc := make(chan string)\n\tvar calls int32\n\tfn := func() (interface{}, error) {\n\t\tatomic.AddInt32(&calls, 1)\n\t\treturn <-c, nil\n\t}\n\n\tconst n = 10\n\tvar wg sync.WaitGroup\n\tfor i := 0; i < n; i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tv, err := g.Do(\"key\", fn)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Do error: %v\", err)\n\t\t\t}\n\t\t\tif v.(string) != \"bar\" {\n\t\t\t\tt.Errorf(\"got %q; want %q\", v, \"bar\")\n\t\t\t}\n\t\t\twg.Done()\n\t\t}()\n\t}\n\ttime.Sleep(100 * time.Millisecond) // let goroutines above block\n\tc <- \"bar\"\n\twg.Wait()\n\tif got := atomic.LoadInt32(&calls); got != 1 {\n\t\tt.Errorf(\"number of calls = %d; want 1\", got)\n\t}\n}\n"
  },
  {
    "path": "sinks.go",
    "content": "/*\nCopyright 2012 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage groupcache\n\nimport (\n\t\"errors\"\n\n\t\"github.com/golang/protobuf/proto\"\n)\n\n// A Sink receives data from a Get call.\n//\n// Implementation of Getter must call exactly one of the Set methods\n// on success.\ntype Sink interface {\n\t// SetString sets the value to s.\n\tSetString(s string) error\n\n\t// SetBytes sets the value to the contents of v.\n\t// The caller retains ownership of v.\n\tSetBytes(v []byte) error\n\n\t// SetProto sets the value to the encoded version of m.\n\t// The caller retains ownership of m.\n\tSetProto(m proto.Message) error\n\n\t// view returns a frozen view of the bytes for caching.\n\tview() (ByteView, error)\n}\n\nfunc cloneBytes(b []byte) []byte {\n\tc := make([]byte, len(b))\n\tcopy(c, b)\n\treturn c\n}\n\nfunc setSinkView(s Sink, v ByteView) error {\n\t// A viewSetter is a Sink that can also receive its value from\n\t// a ByteView. This is a fast path to minimize copies when the\n\t// item was already cached locally in memory (where it's\n\t// cached as a ByteView)\n\ttype viewSetter interface {\n\t\tsetView(v ByteView) error\n\t}\n\tif vs, ok := s.(viewSetter); ok {\n\t\treturn vs.setView(v)\n\t}\n\tif v.b != nil {\n\t\treturn s.SetBytes(v.b)\n\t}\n\treturn s.SetString(v.s)\n}\n\n// StringSink returns a Sink that populates the provided string pointer.\nfunc StringSink(sp *string) Sink {\n\treturn &stringSink{sp: sp}\n}\n\ntype stringSink struct {\n\tsp *string\n\tv  ByteView\n\t// TODO(bradfitz): track whether any Sets were called.\n}\n\nfunc (s *stringSink) view() (ByteView, error) {\n\t// TODO(bradfitz): return an error if no Set was called\n\treturn s.v, nil\n}\n\nfunc (s *stringSink) SetString(v string) error {\n\ts.v.b = nil\n\ts.v.s = v\n\t*s.sp = v\n\treturn nil\n}\n\nfunc (s *stringSink) SetBytes(v []byte) error {\n\treturn s.SetString(string(v))\n}\n\nfunc (s *stringSink) SetProto(m proto.Message) error {\n\tb, err := proto.Marshal(m)\n\tif err != nil {\n\t\treturn err\n\t}\n\ts.v.b = b\n\t*s.sp = string(b)\n\treturn nil\n}\n\n// ByteViewSink returns a Sink that populates a ByteView.\nfunc ByteViewSink(dst *ByteView) Sink {\n\tif dst == nil {\n\t\tpanic(\"nil dst\")\n\t}\n\treturn &byteViewSink{dst: dst}\n}\n\ntype byteViewSink struct {\n\tdst *ByteView\n\n\t// if this code ever ends up tracking that at least one set*\n\t// method was called, don't make it an error to call set\n\t// methods multiple times. Lorry's payload.go does that, and\n\t// it makes sense. The comment at the top of this file about\n\t// \"exactly one of the Set methods\" is overly strict. We\n\t// really care about at least once (in a handler), but if\n\t// multiple handlers fail (or multiple functions in a program\n\t// using a Sink), it's okay to re-use the same one.\n}\n\nfunc (s *byteViewSink) setView(v ByteView) error {\n\t*s.dst = v\n\treturn nil\n}\n\nfunc (s *byteViewSink) view() (ByteView, error) {\n\treturn *s.dst, nil\n}\n\nfunc (s *byteViewSink) SetProto(m proto.Message) error {\n\tb, err := proto.Marshal(m)\n\tif err != nil {\n\t\treturn err\n\t}\n\t*s.dst = ByteView{b: b}\n\treturn nil\n}\n\nfunc (s *byteViewSink) SetBytes(b []byte) error {\n\t*s.dst = ByteView{b: cloneBytes(b)}\n\treturn nil\n}\n\nfunc (s *byteViewSink) SetString(v string) error {\n\t*s.dst = ByteView{s: v}\n\treturn nil\n}\n\n// ProtoSink returns a sink that unmarshals binary proto values into m.\nfunc ProtoSink(m proto.Message) Sink {\n\treturn &protoSink{\n\t\tdst: m,\n\t}\n}\n\ntype protoSink struct {\n\tdst proto.Message // authoritative value\n\ttyp string\n\n\tv ByteView // encoded\n}\n\nfunc (s *protoSink) view() (ByteView, error) {\n\treturn s.v, nil\n}\n\nfunc (s *protoSink) SetBytes(b []byte) error {\n\terr := proto.Unmarshal(b, s.dst)\n\tif err != nil {\n\t\treturn err\n\t}\n\ts.v.b = cloneBytes(b)\n\ts.v.s = \"\"\n\treturn nil\n}\n\nfunc (s *protoSink) SetString(v string) error {\n\tb := []byte(v)\n\terr := proto.Unmarshal(b, s.dst)\n\tif err != nil {\n\t\treturn err\n\t}\n\ts.v.b = b\n\ts.v.s = \"\"\n\treturn nil\n}\n\nfunc (s *protoSink) SetProto(m proto.Message) error {\n\tb, err := proto.Marshal(m)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// TODO(bradfitz): optimize for same-task case more and write\n\t// right through? would need to document ownership rules at\n\t// the same time. but then we could just assign *dst = *m\n\t// here. This works for now:\n\terr = proto.Unmarshal(b, s.dst)\n\tif err != nil {\n\t\treturn err\n\t}\n\ts.v.b = b\n\ts.v.s = \"\"\n\treturn nil\n}\n\n// AllocatingByteSliceSink returns a Sink that allocates\n// a byte slice to hold the received value and assigns\n// it to *dst. The memory is not retained by groupcache.\nfunc AllocatingByteSliceSink(dst *[]byte) Sink {\n\treturn &allocBytesSink{dst: dst}\n}\n\ntype allocBytesSink struct {\n\tdst *[]byte\n\tv   ByteView\n}\n\nfunc (s *allocBytesSink) view() (ByteView, error) {\n\treturn s.v, nil\n}\n\nfunc (s *allocBytesSink) setView(v ByteView) error {\n\tif v.b != nil {\n\t\t*s.dst = cloneBytes(v.b)\n\t} else {\n\t\t*s.dst = []byte(v.s)\n\t}\n\ts.v = v\n\treturn nil\n}\n\nfunc (s *allocBytesSink) SetProto(m proto.Message) error {\n\tb, err := proto.Marshal(m)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn s.setBytesOwned(b)\n}\n\nfunc (s *allocBytesSink) SetBytes(b []byte) error {\n\treturn s.setBytesOwned(cloneBytes(b))\n}\n\nfunc (s *allocBytesSink) setBytesOwned(b []byte) error {\n\tif s.dst == nil {\n\t\treturn errors.New(\"nil AllocatingByteSliceSink *[]byte dst\")\n\t}\n\t*s.dst = cloneBytes(b) // another copy, protecting the read-only s.v.b view\n\ts.v.b = b\n\ts.v.s = \"\"\n\treturn nil\n}\n\nfunc (s *allocBytesSink) SetString(v string) error {\n\tif s.dst == nil {\n\t\treturn errors.New(\"nil AllocatingByteSliceSink *[]byte dst\")\n\t}\n\t*s.dst = []byte(v)\n\ts.v.b = nil\n\ts.v.s = v\n\treturn nil\n}\n\n// TruncatingByteSliceSink returns a Sink that writes up to len(*dst)\n// bytes to *dst. If more bytes are available, they're silently\n// truncated. If fewer bytes are available than len(*dst), *dst\n// is shrunk to fit the number of bytes available.\nfunc TruncatingByteSliceSink(dst *[]byte) Sink {\n\treturn &truncBytesSink{dst: dst}\n}\n\ntype truncBytesSink struct {\n\tdst *[]byte\n\tv   ByteView\n}\n\nfunc (s *truncBytesSink) view() (ByteView, error) {\n\treturn s.v, nil\n}\n\nfunc (s *truncBytesSink) SetProto(m proto.Message) error {\n\tb, err := proto.Marshal(m)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn s.setBytesOwned(b)\n}\n\nfunc (s *truncBytesSink) SetBytes(b []byte) error {\n\treturn s.setBytesOwned(cloneBytes(b))\n}\n\nfunc (s *truncBytesSink) setBytesOwned(b []byte) error {\n\tif s.dst == nil {\n\t\treturn errors.New(\"nil TruncatingByteSliceSink *[]byte dst\")\n\t}\n\tn := copy(*s.dst, b)\n\tif n < len(*s.dst) {\n\t\t*s.dst = (*s.dst)[:n]\n\t}\n\ts.v.b = b\n\ts.v.s = \"\"\n\treturn nil\n}\n\nfunc (s *truncBytesSink) SetString(v string) error {\n\tif s.dst == nil {\n\t\treturn errors.New(\"nil TruncatingByteSliceSink *[]byte dst\")\n\t}\n\tn := copy(*s.dst, v)\n\tif n < len(*s.dst) {\n\t\t*s.dst = (*s.dst)[:n]\n\t}\n\ts.v.b = nil\n\ts.v.s = v\n\treturn nil\n}\n"
  },
  {
    "path": "testpb/test.pb.go",
    "content": "// Code generated by protoc-gen-go.\n// source: test.proto\n// DO NOT EDIT!\n\npackage testpb\n\nimport proto \"github.com/golang/protobuf/proto\"\nimport json \"encoding/json\"\nimport math \"math\"\n\n// Reference proto, json, and math imports to suppress error if they are not otherwise used.\nvar _ = proto.Marshal\nvar _ = &json.SyntaxError{}\nvar _ = math.Inf\n\ntype TestMessage struct {\n\tName             *string `protobuf:\"bytes,1,opt,name=name\" json:\"name,omitempty\"`\n\tCity             *string `protobuf:\"bytes,2,opt,name=city\" json:\"city,omitempty\"`\n\tXXX_unrecognized []byte  `json:\"-\"`\n}\n\nfunc (m *TestMessage) Reset()         { *m = TestMessage{} }\nfunc (m *TestMessage) String() string { return proto.CompactTextString(m) }\nfunc (*TestMessage) ProtoMessage()    {}\n\nfunc (m *TestMessage) GetName() string {\n\tif m != nil && m.Name != nil {\n\t\treturn *m.Name\n\t}\n\treturn \"\"\n}\n\nfunc (m *TestMessage) GetCity() string {\n\tif m != nil && m.City != nil {\n\t\treturn *m.City\n\t}\n\treturn \"\"\n}\n\ntype TestRequest struct {\n\tLower            *string `protobuf:\"bytes,1,req,name=lower\" json:\"lower,omitempty\"`\n\tRepeatCount      *int32  `protobuf:\"varint,2,opt,name=repeat_count,def=1\" json:\"repeat_count,omitempty\"`\n\tXXX_unrecognized []byte  `json:\"-\"`\n}\n\nfunc (m *TestRequest) Reset()         { *m = TestRequest{} }\nfunc (m *TestRequest) String() string { return proto.CompactTextString(m) }\nfunc (*TestRequest) ProtoMessage()    {}\n\nconst Default_TestRequest_RepeatCount int32 = 1\n\nfunc (m *TestRequest) GetLower() string {\n\tif m != nil && m.Lower != nil {\n\t\treturn *m.Lower\n\t}\n\treturn \"\"\n}\n\nfunc (m *TestRequest) GetRepeatCount() int32 {\n\tif m != nil && m.RepeatCount != nil {\n\t\treturn *m.RepeatCount\n\t}\n\treturn Default_TestRequest_RepeatCount\n}\n\ntype TestResponse struct {\n\tValue            *string `protobuf:\"bytes,1,opt,name=value\" json:\"value,omitempty\"`\n\tXXX_unrecognized []byte  `json:\"-\"`\n}\n\nfunc (m *TestResponse) Reset()         { *m = TestResponse{} }\nfunc (m *TestResponse) String() string { return proto.CompactTextString(m) }\nfunc (*TestResponse) ProtoMessage()    {}\n\nfunc (m *TestResponse) GetValue() string {\n\tif m != nil && m.Value != nil {\n\t\treturn *m.Value\n\t}\n\treturn \"\"\n}\n\ntype CacheStats struct {\n\tItems            *int64 `protobuf:\"varint,1,opt,name=items\" json:\"items,omitempty\"`\n\tBytes            *int64 `protobuf:\"varint,2,opt,name=bytes\" json:\"bytes,omitempty\"`\n\tGets             *int64 `protobuf:\"varint,3,opt,name=gets\" json:\"gets,omitempty\"`\n\tHits             *int64 `protobuf:\"varint,4,opt,name=hits\" json:\"hits,omitempty\"`\n\tEvicts           *int64 `protobuf:\"varint,5,opt,name=evicts\" json:\"evicts,omitempty\"`\n\tXXX_unrecognized []byte `json:\"-\"`\n}\n\nfunc (m *CacheStats) Reset()         { *m = CacheStats{} }\nfunc (m *CacheStats) String() string { return proto.CompactTextString(m) }\nfunc (*CacheStats) ProtoMessage()    {}\n\nfunc (m *CacheStats) GetItems() int64 {\n\tif m != nil && m.Items != nil {\n\t\treturn *m.Items\n\t}\n\treturn 0\n}\n\nfunc (m *CacheStats) GetBytes() int64 {\n\tif m != nil && m.Bytes != nil {\n\t\treturn *m.Bytes\n\t}\n\treturn 0\n}\n\nfunc (m *CacheStats) GetGets() int64 {\n\tif m != nil && m.Gets != nil {\n\t\treturn *m.Gets\n\t}\n\treturn 0\n}\n\nfunc (m *CacheStats) GetHits() int64 {\n\tif m != nil && m.Hits != nil {\n\t\treturn *m.Hits\n\t}\n\treturn 0\n}\n\nfunc (m *CacheStats) GetEvicts() int64 {\n\tif m != nil && m.Evicts != nil {\n\t\treturn *m.Evicts\n\t}\n\treturn 0\n}\n\ntype StatsResponse struct {\n\tGets             *int64      `protobuf:\"varint,1,opt,name=gets\" json:\"gets,omitempty\"`\n\tCacheHits        *int64      `protobuf:\"varint,12,opt,name=cache_hits\" json:\"cache_hits,omitempty\"`\n\tFills            *int64      `protobuf:\"varint,2,opt,name=fills\" json:\"fills,omitempty\"`\n\tTotalAlloc       *uint64     `protobuf:\"varint,3,opt,name=total_alloc\" json:\"total_alloc,omitempty\"`\n\tMainCache        *CacheStats `protobuf:\"bytes,4,opt,name=main_cache\" json:\"main_cache,omitempty\"`\n\tHotCache         *CacheStats `protobuf:\"bytes,5,opt,name=hot_cache\" json:\"hot_cache,omitempty\"`\n\tServerIn         *int64      `protobuf:\"varint,6,opt,name=server_in\" json:\"server_in,omitempty\"`\n\tLoads            *int64      `protobuf:\"varint,8,opt,name=loads\" json:\"loads,omitempty\"`\n\tPeerLoads        *int64      `protobuf:\"varint,9,opt,name=peer_loads\" json:\"peer_loads,omitempty\"`\n\tPeerErrors       *int64      `protobuf:\"varint,10,opt,name=peer_errors\" json:\"peer_errors,omitempty\"`\n\tLocalLoads       *int64      `protobuf:\"varint,11,opt,name=local_loads\" json:\"local_loads,omitempty\"`\n\tXXX_unrecognized []byte      `json:\"-\"`\n}\n\nfunc (m *StatsResponse) Reset()         { *m = StatsResponse{} }\nfunc (m *StatsResponse) String() string { return proto.CompactTextString(m) }\nfunc (*StatsResponse) ProtoMessage()    {}\n\nfunc (m *StatsResponse) GetGets() int64 {\n\tif m != nil && m.Gets != nil {\n\t\treturn *m.Gets\n\t}\n\treturn 0\n}\n\nfunc (m *StatsResponse) GetCacheHits() int64 {\n\tif m != nil && m.CacheHits != nil {\n\t\treturn *m.CacheHits\n\t}\n\treturn 0\n}\n\nfunc (m *StatsResponse) GetFills() int64 {\n\tif m != nil && m.Fills != nil {\n\t\treturn *m.Fills\n\t}\n\treturn 0\n}\n\nfunc (m *StatsResponse) GetTotalAlloc() uint64 {\n\tif m != nil && m.TotalAlloc != nil {\n\t\treturn *m.TotalAlloc\n\t}\n\treturn 0\n}\n\nfunc (m *StatsResponse) GetMainCache() *CacheStats {\n\tif m != nil {\n\t\treturn m.MainCache\n\t}\n\treturn nil\n}\n\nfunc (m *StatsResponse) GetHotCache() *CacheStats {\n\tif m != nil {\n\t\treturn m.HotCache\n\t}\n\treturn nil\n}\n\nfunc (m *StatsResponse) GetServerIn() int64 {\n\tif m != nil && m.ServerIn != nil {\n\t\treturn *m.ServerIn\n\t}\n\treturn 0\n}\n\nfunc (m *StatsResponse) GetLoads() int64 {\n\tif m != nil && m.Loads != nil {\n\t\treturn *m.Loads\n\t}\n\treturn 0\n}\n\nfunc (m *StatsResponse) GetPeerLoads() int64 {\n\tif m != nil && m.PeerLoads != nil {\n\t\treturn *m.PeerLoads\n\t}\n\treturn 0\n}\n\nfunc (m *StatsResponse) GetPeerErrors() int64 {\n\tif m != nil && m.PeerErrors != nil {\n\t\treturn *m.PeerErrors\n\t}\n\treturn 0\n}\n\nfunc (m *StatsResponse) GetLocalLoads() int64 {\n\tif m != nil && m.LocalLoads != nil {\n\t\treturn *m.LocalLoads\n\t}\n\treturn 0\n}\n\ntype Empty struct {\n\tXXX_unrecognized []byte `json:\"-\"`\n}\n\nfunc (m *Empty) Reset()         { *m = Empty{} }\nfunc (m *Empty) String() string { return proto.CompactTextString(m) }\nfunc (*Empty) ProtoMessage()    {}\n\nfunc init() {\n}\n"
  },
  {
    "path": "testpb/test.proto",
    "content": "/*\nCopyright 2012 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nsyntax = \"proto2\";\n\npackage testpb;\n\nmessage TestMessage {\n  optional string name = 1;\n  optional string city = 2;\n}\n\nmessage TestRequest {\n  required string lower = 1; // to be returned upper case\n  optional int32 repeat_count = 2 [default = 1]; // .. this many times\n}\n\nmessage TestResponse {\n  optional string value = 1;\n}\n\nmessage CacheStats {\n  optional int64 items = 1;\n  optional int64 bytes = 2;\n  optional int64 gets = 3;\n  optional int64 hits = 4;\n  optional int64 evicts = 5;\n}\n\nmessage StatsResponse {\n  optional int64 gets = 1;\n  optional int64 cache_hits = 12;\n  optional int64 fills = 2;\n  optional uint64 total_alloc = 3;\n  optional CacheStats main_cache = 4;\n  optional CacheStats hot_cache = 5;\n  optional int64 server_in = 6;\n  optional int64 loads = 8;\n  optional int64 peer_loads = 9;\n  optional int64 peer_errors = 10;\n  optional int64 local_loads = 11;\n}\n\nmessage Empty {}\n\nservice GroupCacheTest {\n  rpc InitPeers(Empty) returns (Empty) {};\n  rpc Get(TestRequest) returns (TestResponse) {};\n  rpc GetStats(Empty) returns (StatsResponse) {};\n}\n"
  }
]