The ChunkedInsert object is great for speeding up inserts. This commit
does the same for updates. It chunks up updates then sends them to the
table's `update_many` in bundles that contain updates to the same
fields.
In doing this the ChunkedInsert and new ChunkedUpdate were refactored to
inherit from a common `_Chunker` object that does most of the chunking
logic. This should also make it simple to add a ChunkedUpsert object.
This commit adds an optional `callback` argument to the `ChunkedInsert`
object. This callback is a callable object which gets called before the
chunked insert happens. This is useful for clearing any local caches
that may be in place to deal with the eventual consistency resulting
from the delayed nature of the chunked inserts.
For example,
```
cache = set()
chunked_table = ChunkedInsert(table, callback=lambda queue: cache.clear())
while True:
data = get_data_id()
key = data['key']
if key in cache or table.find_one(key=key)
continue
cache.add(key)
chunked_table.insert(data)
```