github.com/lingyao2333/mo-zero@v1.4.1/core/stores/mongoc/cachedcollection_test.go (about) 1 package mongoc 2 3 import ( 4 "encoding/json" 5 "errors" 6 "io" 7 "log" 8 "os" 9 "runtime" 10 "sync" 11 "sync/atomic" 12 "testing" 13 "time" 14 15 "github.com/globalsign/mgo" 16 "github.com/globalsign/mgo/bson" 17 "github.com/lingyao2333/mo-zero/core/stat" 18 "github.com/lingyao2333/mo-zero/core/stores/cache" 19 "github.com/lingyao2333/mo-zero/core/stores/mongo" 20 "github.com/lingyao2333/mo-zero/core/stores/redis" 21 "github.com/lingyao2333/mo-zero/core/stores/redis/redistest" 22 "github.com/stretchr/testify/assert" 23 ) 24 25 const dummyCount = 10 26 27 func init() { 28 stat.SetReporter(nil) 29 } 30 31 func TestCollection_Count(t *testing.T) { 32 resetStats() 33 r, clean, err := redistest.CreateRedis() 34 assert.Nil(t, err) 35 defer clean() 36 37 cach := cache.NewNode(r, singleFlight, stats, mgo.ErrNotFound) 38 c := newCollection(dummyConn{}, cach) 39 val, err := c.Count("any") 40 assert.Nil(t, err) 41 assert.Equal(t, dummyCount, val) 42 43 var value string 44 assert.Nil(t, r.Set("any", `"foo"`)) 45 assert.Nil(t, c.GetCache("any", &value)) 46 assert.Equal(t, "foo", value) 47 assert.Nil(t, c.DelCache("any")) 48 49 assert.Nil(t, c.SetCache("any", "bar")) 50 assert.Nil(t, c.FindAllNoCache(&value, "any", func(query mongo.Query) mongo.Query { 51 return query 52 })) 53 assert.Nil(t, c.FindOne(&value, "any", "foo")) 54 assert.Equal(t, "bar", value) 55 assert.Nil(t, c.DelCache("any")) 56 c = newCollection(dummyConn{val: `"bar"`}, cach) 57 assert.Nil(t, c.FindOne(&value, "any", "foo")) 58 assert.Equal(t, "bar", value) 59 assert.Nil(t, c.FindOneNoCache(&value, "foo")) 60 assert.Equal(t, "bar", value) 61 assert.Nil(t, c.FindOneId(&value, "anyone", "foo")) 62 assert.Equal(t, "bar", value) 63 assert.Nil(t, c.FindOneIdNoCache(&value, "foo")) 64 assert.Equal(t, "bar", value) 65 assert.Nil(t, c.Insert("foo")) 66 assert.Nil(t, c.Pipe("foo")) 67 assert.Nil(t, c.Remove("any")) 68 assert.Nil(t, c.RemoveId("any")) 69 _, err = c.RemoveAll("any") 70 assert.Nil(t, err) 71 assert.Nil(t, c.Update("foo", "bar")) 72 assert.Nil(t, c.UpdateId("foo", "bar")) 73 _, err = c.Upsert("foo", "bar") 74 assert.Nil(t, err) 75 76 c = newCollection(dummyConn{ 77 val: `"bar"`, 78 removeErr: errors.New("any"), 79 }, cach) 80 assert.NotNil(t, c.Remove("any")) 81 _, err = c.RemoveAll("any", "bar") 82 assert.NotNil(t, err) 83 assert.NotNil(t, c.RemoveId("any")) 84 85 c = newCollection(dummyConn{ 86 val: `"bar"`, 87 updateErr: errors.New("any"), 88 }, cach) 89 assert.NotNil(t, c.Update("foo", "bar")) 90 assert.NotNil(t, c.UpdateId("foo", "bar")) 91 _, err = c.Upsert("foo", "bar") 92 assert.NotNil(t, err) 93 } 94 95 func TestStat(t *testing.T) { 96 resetStats() 97 r, clean, err := redistest.CreateRedis() 98 assert.Nil(t, err) 99 defer clean() 100 101 cach := cache.NewNode(r, singleFlight, stats, mgo.ErrNotFound) 102 c := newCollection(dummyConn{}, cach).(*cachedCollection) 103 104 for i := 0; i < 10; i++ { 105 var str string 106 if err = c.cache.Take(&str, "name", func(v interface{}) error { 107 *v.(*string) = "zero" 108 return nil 109 }); err != nil { 110 t.Error(err) 111 } 112 } 113 114 assert.Equal(t, uint64(10), atomic.LoadUint64(&stats.Total)) 115 assert.Equal(t, uint64(9), atomic.LoadUint64(&stats.Hit)) 116 } 117 118 func TestStatCacheFails(t *testing.T) { 119 resetStats() 120 log.SetOutput(io.Discard) 121 defer log.SetOutput(os.Stdout) 122 123 r := redis.New("localhost:59999") 124 cach := cache.NewNode(r, singleFlight, stats, mgo.ErrNotFound) 125 c := newCollection(dummyConn{}, cach) 126 127 for i := 0; i < 20; i++ { 128 var str string 129 err := c.FindOne(&str, "name", bson.M{}) 130 assert.NotNil(t, err) 131 } 132 133 assert.Equal(t, uint64(20), atomic.LoadUint64(&stats.Total)) 134 assert.Equal(t, uint64(0), atomic.LoadUint64(&stats.Hit)) 135 assert.Equal(t, uint64(20), atomic.LoadUint64(&stats.Miss)) 136 assert.Equal(t, uint64(0), atomic.LoadUint64(&stats.DbFails)) 137 } 138 139 func TestStatDbFails(t *testing.T) { 140 resetStats() 141 r, clean, err := redistest.CreateRedis() 142 assert.Nil(t, err) 143 defer clean() 144 145 cach := cache.NewNode(r, singleFlight, stats, mgo.ErrNotFound) 146 c := newCollection(dummyConn{}, cach).(*cachedCollection) 147 148 for i := 0; i < 20; i++ { 149 var str string 150 err := c.cache.Take(&str, "name", func(v interface{}) error { 151 return errors.New("db failed") 152 }) 153 assert.NotNil(t, err) 154 } 155 156 assert.Equal(t, uint64(20), atomic.LoadUint64(&stats.Total)) 157 assert.Equal(t, uint64(0), atomic.LoadUint64(&stats.Hit)) 158 assert.Equal(t, uint64(20), atomic.LoadUint64(&stats.DbFails)) 159 } 160 161 func TestStatFromMemory(t *testing.T) { 162 resetStats() 163 r, clean, err := redistest.CreateRedis() 164 assert.Nil(t, err) 165 defer clean() 166 167 cach := cache.NewNode(r, singleFlight, stats, mgo.ErrNotFound) 168 c := newCollection(dummyConn{}, cach).(*cachedCollection) 169 170 var all sync.WaitGroup 171 var wait sync.WaitGroup 172 all.Add(10) 173 wait.Add(4) 174 go func() { 175 var str string 176 if err := c.cache.Take(&str, "name", func(v interface{}) error { 177 *v.(*string) = "zero" 178 return nil 179 }); err != nil { 180 t.Error(err) 181 } 182 wait.Wait() 183 runtime.Gosched() 184 all.Done() 185 }() 186 187 for i := 0; i < 4; i++ { 188 go func() { 189 var str string 190 wait.Done() 191 if err := c.cache.Take(&str, "name", func(v interface{}) error { 192 *v.(*string) = "zero" 193 return nil 194 }); err != nil { 195 t.Error(err) 196 } 197 all.Done() 198 }() 199 } 200 for i := 0; i < 5; i++ { 201 go func() { 202 var str string 203 if err := c.cache.Take(&str, "name", func(v interface{}) error { 204 *v.(*string) = "zero" 205 return nil 206 }); err != nil { 207 t.Error(err) 208 } 209 all.Done() 210 }() 211 } 212 all.Wait() 213 214 assert.Equal(t, uint64(10), atomic.LoadUint64(&stats.Total)) 215 assert.Equal(t, uint64(9), atomic.LoadUint64(&stats.Hit)) 216 } 217 218 func resetStats() { 219 atomic.StoreUint64(&stats.Total, 0) 220 atomic.StoreUint64(&stats.Hit, 0) 221 atomic.StoreUint64(&stats.Miss, 0) 222 atomic.StoreUint64(&stats.DbFails, 0) 223 } 224 225 type dummyConn struct { 226 val string 227 removeErr error 228 updateErr error 229 } 230 231 func (c dummyConn) Find(query interface{}) mongo.Query { 232 return dummyQuery{val: c.val} 233 } 234 235 func (c dummyConn) FindId(id interface{}) mongo.Query { 236 return dummyQuery{val: c.val} 237 } 238 239 func (c dummyConn) Insert(docs ...interface{}) error { 240 return nil 241 } 242 243 func (c dummyConn) Remove(selector interface{}) error { 244 return c.removeErr 245 } 246 247 func (dummyConn) Pipe(pipeline interface{}) mongo.Pipe { 248 return nil 249 } 250 251 func (c dummyConn) RemoveAll(selector interface{}) (*mgo.ChangeInfo, error) { 252 return nil, c.removeErr 253 } 254 255 func (c dummyConn) RemoveId(id interface{}) error { 256 return c.removeErr 257 } 258 259 func (c dummyConn) Update(selector, update interface{}) error { 260 return c.updateErr 261 } 262 263 func (c dummyConn) UpdateId(id, update interface{}) error { 264 return c.updateErr 265 } 266 267 func (c dummyConn) Upsert(selector, update interface{}) (*mgo.ChangeInfo, error) { 268 return nil, c.updateErr 269 } 270 271 type dummyQuery struct { 272 val string 273 } 274 275 func (d dummyQuery) All(result interface{}) error { 276 return nil 277 } 278 279 func (d dummyQuery) Apply(change mgo.Change, result interface{}) (*mgo.ChangeInfo, error) { 280 return nil, nil 281 } 282 283 func (d dummyQuery) Count() (int, error) { 284 return dummyCount, nil 285 } 286 287 func (d dummyQuery) Distinct(key string, result interface{}) error { 288 return nil 289 } 290 291 func (d dummyQuery) Explain(result interface{}) error { 292 return nil 293 } 294 295 func (d dummyQuery) For(result interface{}, f func() error) error { 296 return nil 297 } 298 299 func (d dummyQuery) MapReduce(job *mgo.MapReduce, result interface{}) (*mgo.MapReduceInfo, error) { 300 return nil, nil 301 } 302 303 func (d dummyQuery) One(result interface{}) error { 304 return json.Unmarshal([]byte(d.val), result) 305 } 306 307 func (d dummyQuery) Batch(n int) mongo.Query { 308 return d 309 } 310 311 func (d dummyQuery) Collation(collation *mgo.Collation) mongo.Query { 312 return d 313 } 314 315 func (d dummyQuery) Comment(comment string) mongo.Query { 316 return d 317 } 318 319 func (d dummyQuery) Hint(indexKey ...string) mongo.Query { 320 return d 321 } 322 323 func (d dummyQuery) Iter() mongo.Iter { 324 return &mgo.Iter{} 325 } 326 327 func (d dummyQuery) Limit(n int) mongo.Query { 328 return d 329 } 330 331 func (d dummyQuery) LogReplay() mongo.Query { 332 return d 333 } 334 335 func (d dummyQuery) Prefetch(p float64) mongo.Query { 336 return d 337 } 338 339 func (d dummyQuery) Select(selector interface{}) mongo.Query { 340 return d 341 } 342 343 func (d dummyQuery) SetMaxScan(n int) mongo.Query { 344 return d 345 } 346 347 func (d dummyQuery) SetMaxTime(duration time.Duration) mongo.Query { 348 return d 349 } 350 351 func (d dummyQuery) Skip(n int) mongo.Query { 352 return d 353 } 354 355 func (d dummyQuery) Snapshot() mongo.Query { 356 return d 357 } 358 359 func (d dummyQuery) Sort(fields ...string) mongo.Query { 360 return d 361 } 362 363 func (d dummyQuery) Tail(timeout time.Duration) mongo.Iter { 364 return &mgo.Iter{} 365 }