kelindar / bitmap

Simple dense bitmap index in Go with binary operators
MIT License
306 stars 23 forks source link

Kelindar fails under highload. 1-3/400k requests #23

Closed marniks7 closed 2 years ago

marniks7 commented 2 years ago

Hi, After upgrade from v1.1.4 to v1.2.1 I got errors under highload tests

Test Case:

git clone https://github.com/marniks7/bitmap-usage/
cd bitmap-usage
git checkout update-dependencies
go test ./runner/... -run PerformanceWrkExperiments -covermode=atomic -short -test.v -count=1

AR: (wrk, client side), 3 errors per 390k+ requests

  391214 requests in 10.10s, 76.38MB read
  Non-2xx or 3xx responses: 3

ER: Non-2xx or 3xx responses should be absent

AR: (errors on application side)

2022-04-10T11:01:28-04:00 ERR Unable to find price id error="unable find price, no default and >1 found"
2022-04-10T11:01:32-04:00 ERR Unable to find price id error="unable find price, no default and >1 found"
2022-04-10T11:01:34-04:00 ERR Unable to find price id error="unable find price, no default and >1 found"

ER: there should be 0 errors on application side

Code: https://github.com/marniks7/bitmap-usage/blob/update-dependencies/index-kelindar/searchv2.go Code for breakpoint:

 else {
        return 0, ErrUnableToFindPriceMoreThenOneNoDefault
    }

List of used operations:

tx.index.And
tx.index.Count()
bm.Clone

Debug:

  1. KELINDAR32=true FIBER=true go run main.go
  2. make wrk-kelindar-t2-c20
marniks7 commented 2 years ago

Is it possible that this issue is on application side? yes, there is a chance, for example due to sync.Pool, but it is not reproduced on another version(s) of kelindar.

After testing with different versions - the problem introduced in v1.2.0.

BTW, what did you use for performance testing here https://github.com/kelindar/bitmap/releases/tag/v1.2.0 ?

marniks7 commented 2 years ago

This behavior is due to Clone function and removed code *into = (*into)[:len(dst)]. This is fine, i have changed the application code

Before: (not exact, but idea)

func (h *Holder) release(tx *Tx) {
    h.empty.Clone(&tx.index)
    h.bitmapPool.Put(tx)
}

After

func (h *Holder) release(tx *Tx) {
    clone := h.empty.Clone(&tx.index)
    tx.index = clone
    h.bitmapPool.Put(tx)
}