Home | Recent Changes | Search | Log in

Proposal

General Synopsis

Invalidation of a tag would basically be a ``global_generation = ++tags[tag]'' kind of operation.

Each cache item contains a space for pointers to tags with their individual generation numbers and a local generation number.

Adding an existing tag to an item must not cause any modification to the item (i.e. check first).

Each time an item is requested from a cache, the local generation number is compared against the global generation number. If it differs, each tag is checked to ensure the tag generation number equals the number stored for that tag.

If they're all the same, the local generation number is set to the global generation number.

If they're different, this record doesn't exist.

Protocol should not be updated to support ‘tags’ under add/set/replace/cas.

Allocations / Optimizations

Structures in the tag hash should definitely be reusable in a free list, like most of the other structures. Having one or more per key could be massive suck if you're storing small items. Otherwise the goal should still be to avoid malloc/free if at all possible.

Presize the tag table?
Free list the tag name/version linked list?

Tag array per item?
8 bytes per item overhead - Counter & Pointer. Whether the pointer is a linked list or an array, the overhead is fixed for non-tag users.
Realloc the item header?

Tag support as experimental
Could probably be a config (not ./configure) option though, and avoid that memory overhead.

Lock-free hashtable
The hashtable itself can at worst have a lock per bucket. There's an implementation of a lock-free hashtable in java that should be possible in C as well. Perhaps we could just leave this optimization to those who need it since the overlap between people who really need MT and people who really wants tags seems to be quite small so far.

Each tag will have a reference count that is increment every time the tag is successfully added to an item (i.e. must not increment if an item lookup fails or the item already has the tag).

When the item is deallocated, all tags should be decremented for that item.

When the tags reference count hits zero, we'll pull it from the global map.

Memory churn may be an issue. Someone who knows allocators better than I do can decide what to do here.

Increments are not atomic across SMP/multi-core.

So, given a facility for cache line synchronization
and a CAS, I imagine you'll end up with a lot of code that looks like
this (from Java's AtomicInteger):

    public final int addAndGet(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return next;
        }
    }

glib has something similar as well. It's not guaranteed to be lock-free, but it can be done on a few platforms anyway.

Page Last Updated: Mar 14 9:46pm by David Phillips


Log in - Socialtext v3.1.0.0