[EDIT] - A couple different options to choose from:
1 - XDCR from collection A to collection B, with a 30-day TTL set on collection B: this is the simplest and has the least moving parts, but is also the least flexible if you need more complex logic or need to change in the future
2 - An eventing function listening to collection A and writing to collection B with a 30-day TTL (no XDCR): this puts the onus on the eventing function to move the data but gives you the most flexibility over logic and updating the behaviour.
3 - XDCR from collection A to collection B, with an eventing function listening to collection B to set the TTL: this is the most complex in terms of moving parts, but gives you the most flexibility in terms of combining different functionality.
@bellpumpkin Parry gave some solid information, I do want to point out a few additional items.
In Eventing using an advanced bucket accessor to set the TTL can be 5X faster.
Two variants of this function are available - a 6.6 version that relies on SQL++ and a 6.6.1+/7.0.0+ version (this Function) that directly sets the expiration. You can completely avoid N1QL(…) and use couchbase.replace(bucket_binding, meta, doc) as the advancedDocControlledSelfExpiry variant is much faster.
var ttlMs = docTimeStampMs + keepDocForMs;
var expiryDate = new Date(ttlMs);
var res = couchbase.replace(src_col,{"id":meta.id,"expiry_date":expiryDate},doc);
In future Couchbase releases starting with 7.6 we will be implemented a ‘touch’ function which can operate than using the advanced bucket accessor method (2X faster on 5k byte documents and 50X faster on very large documents 1M byte documents).
var ttlMs = docTimeStampMs + keepDocForMs;
var expiryDate = new Date(ttlMs);
var res = couchbase.touch(src_col, {"id": meta.id, "expiry_date": expiryDate});