(t *testing.T)
| 196 | } |
| 197 | |
| 198 | func TestConcurrency(t *testing.T) { |
| 199 | t.Parallel() |
| 200 | ctx := dbauthz.AsFileReader(t.Context()) |
| 201 | |
| 202 | const fileSize = 10 |
| 203 | var fetches atomic.Int64 |
| 204 | reg := prometheus.NewRegistry() |
| 205 | |
| 206 | dbM := dbmock.NewMockStore(gomock.NewController(t)) |
| 207 | dbM.EXPECT().GetFileByID(gomock.Any(), gomock.Any()).DoAndReturn(func(mTx context.Context, fileID uuid.UUID) (database.File, error) { |
| 208 | fetches.Add(1) |
| 209 | // Wait long enough before returning to make sure that all the goroutines |
| 210 | // will be waiting in line, ensuring that no one duplicated a fetch. |
| 211 | time.Sleep(testutil.IntervalMedium) |
| 212 | return database.File{ |
| 213 | Data: make([]byte, fileSize), |
| 214 | }, nil |
| 215 | }).AnyTimes() |
| 216 | |
| 217 | c := files.New(reg, &coderdtest.FakeAuthorizer{}) |
| 218 | |
| 219 | batches := 1000 |
| 220 | groups := make([]*errgroup.Group, 0, batches) |
| 221 | for range batches { |
| 222 | groups = append(groups, new(errgroup.Group)) |
| 223 | } |
| 224 | |
| 225 | // Call Acquire with a unique ID per batch, many times per batch, with many |
| 226 | // batches all in parallel. This is pretty much the worst-case scenario: |
| 227 | // thousands of concurrent reads, with both warm and cold loads happening. |
| 228 | batchSize := 10 |
| 229 | for _, g := range groups { |
| 230 | id := uuid.New() |
| 231 | for range batchSize { |
| 232 | g.Go(func() error { |
| 233 | // We don't bother to Release these references because the Cache will be |
| 234 | // released at the end of the test anyway. |
| 235 | _, err := c.Acquire(ctx, dbM, id) |
| 236 | return err |
| 237 | }) |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | for _, g := range groups { |
| 242 | require.NoError(t, g.Wait()) |
| 243 | } |
| 244 | require.Equal(t, int64(batches), fetches.Load()) |
| 245 | |
| 246 | // Verify all the counts & metrics are correct. |
| 247 | require.Equal(t, batches, c.Count()) |
| 248 | require.Equal(t, batches*fileSize, promhelp.GaugeValue(t, reg, cachePromMetricName("open_files_size_bytes_current"), nil)) |
| 249 | require.Equal(t, batches*fileSize, promhelp.CounterValue(t, reg, cachePromMetricName("open_files_size_bytes_total"), nil)) |
| 250 | require.Equal(t, batches, promhelp.GaugeValue(t, reg, cachePromMetricName("open_files_current"), nil)) |
| 251 | require.Equal(t, batches, promhelp.CounterValue(t, reg, cachePromMetricName("open_files_total"), nil)) |
| 252 | require.Equal(t, batches*batchSize, promhelp.GaugeValue(t, reg, cachePromMetricName("open_file_refs_current"), nil)) |
| 253 | hit, miss := promhelp.CounterValue(t, reg, cachePromMetricName("open_file_refs_total"), prometheus.Labels{"hit": "false"}), |
| 254 | promhelp.CounterValue(t, reg, cachePromMetricName("open_file_refs_total"), prometheus.Labels{"hit": "true"}) |
| 255 | require.Equal(t, batches*batchSize, hit+miss) |
nothing calls this directly
no test coverage detected