Which Replicated Tables Got Changes in BDR Replication
My favourite feature of BDR by 2ndQuadrant is “multi master table level selective replication”. Currently, I am preparing a plan to switch from trigger based to low level BDR replication for better performance.
But, how can I detect which replicated tables got changes (needed for memcached invalidation, for example). I can not trap it inside always-enabled-trigger any longer. An alternative is to modify BDR plugin c source code. Thanks to 2ndQuadrant team for clear source code. Following code is my modification for bdr_apply.c:
- Module level variable and function declaration:
- Variable initialization at the end of process_remote_begin:
- Modify a bit declaration and definition of read_rel function to catch tablename:
- Declare variable char *tablename inside process_remote_insert, process_remote_update and process_remote_delete. Add its address (&tablename) as third argument for read_real function invocation. Then, register the tablename by calling my append_dtables just after simple_heap_insert, simple_heap_update and simple_heap_delete.
- Call outside function to notify which tables got changes inside process_remote_commit function, somewhere after a call to CommitTransactionCommand.
- And finally, this is my append_dtables definition. Note that memory is allocated in MessageContext so it will be freed automatically in bdr_apply_work main loop.
#define DTABLECHUNK 5
static char** dtables;
static int dtablessize;
static int dtablescount;
static void append_dtables(char* t);
static void
process_remote_begin(StringInfo s) {
...
dtables=NULL;
dtablessize=dtablescount=0;
}
static BDRRelation *
read_rel(StringInfo s, LOCKMODE mode, char** dtablename){
int relnamelen;
int nspnamelen;
RangeVar* rv;
Oid relid;
MemoryContext oldcontext;
rv = makeNode(RangeVar);
nspnamelen = pq_getmsgint(s, 2);
rv->schemaname = (char *) pq_getmsgbytes(s, nspnamelen);
relnamelen = pq_getmsgint(s, 2);
rv->relname = (char *) pq_getmsgbytes(s, relnamelen);
oldcontext=MemoryContextSwitchTo(MessageContext);
//nspnamelen and relnamelen is the length of the name including terminating zero
*dtablename=(char*)palloc( (nspnamelen+relnamelen) * sizeof(char) );
MemoryContextSwitchTo(oldcontext);
sprintf(*dtablename, "%s.%s", rv->schemaname, rv->relname);
relid = RangeVarGetRelidExtended(rv, mode, false, false, NULL, NULL);
return bdr_heap_open(relid, NoLock);
}
for(i=0;dtables!=NULL && i<dtablescount;i++) {
//any function call to notify dtables[i] got changes
}
static void
append_dtables(char* t) {
int i, allocsize;
MemoryContext oldcontext;
for(i=0;i<dtablescount;i++) {
if(!strcmp(dtables[i], t)) {//ensure unique table registration
oldcontext=MemoryContextSwitchTo(MessageContext);
pfree(t);
MemoryContextSwitchTo(oldcontext);
return;
}
}
if(dtablessize<++dtablescount) {
dtablessize+=DTABLECHUNK;
allocsize=dtablessize * sizeof(char*);
oldcontext=MemoryContextSwitchTo(MessageContext);
if(dtables==NULL)
dtables=(char*)palloc(allocsize);
else
dtables=(char**)repalloc(dtables, allocsize);
MemoryContextSwitchTo(oldcontext);
}
dtables[dtablescount-1]=t;
}
What I love from open source project is that there is always a way to tweak to suit my development framework.