Charts the performance of MoreSQL lag on a Heroku dyno.

Latency From Mongo->Postgres As Measured by Postgres Trigger

Methodology

Setup postgres trigger that measures latency between updated_at field and NOW(). Then trigger many (28k) updates. Record the trigger payloads and plot using RMarkdown.

For this measurement, we setup a postgres trigger that fires when any record on given collection is updated. We then updated the 28k records in that collection.

Using a postgres trigger combined with object touch in mongo allows us to see millisecond precision, which is not possible using bson.MongoTimestamp data.

Trigger in Postgres

-- Trigger for pushing data onto notify channel
CREATE OR REPLACE FUNCTION notify_row_changes()
    RETURNS trigger AS $$
DECLARE
    current_row RECORD;
    old_row json;
    new_row json;
BEGIN
    IF TG_OP IN ('INSERT', 'UPDATE') THEN
        current_row := NEW;
        new_row := row_to_json(NEW);
    ELSE
        current_row := OLD;
        new_row := '{}'::json;
    END IF;
    IF TG_OP = 'INSERT' THEN
        old_row := '{}'::json;
    ELSEIF TG_OP IN ('UPDATE', 'DELETE') THEN
        old_row := row_to_json(OLD);
    END IF;
    PERFORM pg_notify(
        'row_changes',
        json_build_object(
            'timestamp', NOW(),
            'uuid', uuid_generate_v4(),
            'table', TG_TABLE_NAME,
            'action', TG_OP,
            '_id', current_row._id,
            'updated_at', current_row.updated_at,
            'ms_lag', extract(MILLISECONDS from INTERVAL '08:00' + NOW() - current_row.updated_at)
        )::text
    );
    RETURN current_row;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER changes_to_row_trigger BEFORE insert or update or delete ON sample_items
FOR EACH ROW EXECUTE PROCEDURE notify_row_changes();

ms_lag was calculated as follows:

extract(MILLISECONDS from INTERVAL '08:00' + NOW() - updated_at) as ms_lag

The trigger output

NOTIFY’s output was observed using Postgres’s LISTEN functionality and a payload was delivered in the following schema:

{
  "timestamp": "2017-02-24T00:08:20.91831+00:00",
  "uuid": "e25ec752-7de3-4de0-b764-bdac9af1a05b",
  "table": "sample_items",
  "action": "INSERT",
  "_id": "4f60c45452e7520003000018",
  "updated_at": "2017-02-24T00:08:20.371",
  "ms_lag": 547.31
}

Results

df <- build_df("ms_lag_from_28000_entries_calculated_by_trigger.log")
print_quantile(df)
[1] "Mean latency: 585.585214235771"
[1] "Quantile by % Chance Latency will be <= this duration (ms)"
      25%       50%       75%       90%       95%       98%       99%      100% 
 383.3840  529.2315  713.8370 1063.5870 1194.5130 1288.7950 1330.8820 1476.4480 
print_histogram(df, "Histogram of ms_lag vs count", "ms_lag", "count")

Conclusion

Looking at the Quantile percentages when testing out MoreSQL, 50% of requests come through with 529ms or less of lag. 90% of requests were processed in trigger with 1063ms of lag or less. 100% of requests came through in 1500ms of lag or less.

LS0tCnRpdGxlOiAiUGVyZm9ybWFuY2Ugb2YgTW9yZVNRTCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKQ2hhcnRzIHRoZSBwZXJmb3JtYW5jZSBvZiBNb3JlU1FMIGxhZyBvbiBhIEhlcm9rdSBkeW5vLgoKYGBge3IgaW5jbHVkZT1GQUxTRX0KYnVpbGRfZGYgPC0gZnVuY3Rpb24oZmlsZW5hbWUpIHsKICBmdWxsX2ZpbGVuYW1lIDwtIHBhc3RlMChTeXMuZ2V0ZW52KCJIT01FIiksICIvZ28vc3JjL2dpdGh1Yi5jb20venBoL21vcmVzcWwvYmVuY2htYXJrcy8iLCBmaWxlbmFtZSkKICB2IDwtIHJlYWRMaW5lcyhmdWxsX2ZpbGVuYW1lKQogIGRmID0gZGF0YS5mcmFtZShsYXRlbmN5ID0gYXMubnVtZXJpYyh2KSkKICBsIDwtIGFzLm51bWVyaWModikKICBkZgp9CgpwcmludF9xdWFudGlsZSA8LSBmdW5jdGlvbihkZikgewogIHByaW50KHBhc3RlMCgiTWVhbiBsYXRlbmN5OiAiLCBtZWFuKGFzLm51bWVyaWMoZGYkbGF0ZW5jeSkpKSkKICBxdWFudCA8LSBxdWFudGlsZShhcy5udW1lcmljKGRmJGxhdGVuY3kpLCBjKC4yNSwgLjUwLCAuNzUsIC45MCwgLjk1LCAuOTgsIC45OSwgMS4wKSkKICBwcmludCgiUXVhbnRpbGUgYnkgJSBDaGFuY2UgTGF0ZW5jeSB3aWxsIGJlIDw9IHRoaXMgZHVyYXRpb24gKG1zKSIpCiAgcHJpbnQocXVhbnQpCn0KCnByaW50X2hpc3RvZ3JhbSA8LSBmdW5jdGlvbihkZiwgdGl0bGUsIHhsYWJlbCwgeWxhYmVsKXsKICBoaXN0KGFzLm51bWVyaWMoZGYkbGF0ZW5jeSksIHhsYWI9eGxhYmVsLCB5bGFiPXlsYWJlbCwgbWFpbj10aXRsZSkKfQoKYGBgCgojIExhdGVuY3kgRnJvbSBNb25nby0+UG9zdGdyZXMgQXMgTWVhc3VyZWQgYnkgUG9zdGdyZXMgVHJpZ2dlcgoKIyMgTWV0aG9kb2xvZ3kKClNldHVwIHBvc3RncmVzIHRyaWdnZXIgdGhhdCBtZWFzdXJlcyBsYXRlbmN5IGJldHdlZW4gdXBkYXRlZF9hdCBmaWVsZCBhbmQgYE5PVygpYC4gVGhlbiB0cmlnZ2VyIG1hbnkgKDI4aykgdXBkYXRlcy4gUmVjb3JkIHRoZSB0cmlnZ2VyIHBheWxvYWRzIGFuZCBwbG90IHVzaW5nIFJNYXJrZG93bi4KCkZvciB0aGlzIG1lYXN1cmVtZW50LCB3ZSBzZXR1cCBhIHBvc3RncmVzIHRyaWdnZXIgdGhhdCBmaXJlcyB3aGVuIGFueSByZWNvcmQgb24gZ2l2ZW4gY29sbGVjdGlvbiBpcyB1cGRhdGVkLiBXZSB0aGVuIHVwZGF0ZWQgdGhlIDI4ayByZWNvcmRzIGluIHRoYXQgY29sbGVjdGlvbi4KClVzaW5nIGEgcG9zdGdyZXMgdHJpZ2dlciBjb21iaW5lZCB3aXRoIG9iamVjdCB0b3VjaCBpbiBtb25nbyBhbGxvd3MgdXMgdG8gc2VlIG1pbGxpc2Vjb25kIHByZWNpc2lvbiwgd2hpY2ggaXMgbm90IHBvc3NpYmxlIHVzaW5nIGJzb24uTW9uZ29UaW1lc3RhbXAgZGF0YS4KCiMjIFRyaWdnZXIgaW4gUG9zdGdyZXMKYGBgc3FsCi0tIFRyaWdnZXIgZm9yIHB1c2hpbmcgZGF0YSBvbnRvIG5vdGlmeSBjaGFubmVsCkNSRUFURSBPUiBSRVBMQUNFIEZVTkNUSU9OIG5vdGlmeV9yb3dfY2hhbmdlcygpCiAgICBSRVRVUk5TIHRyaWdnZXIgQVMgJCQKREVDTEFSRQogICAgY3VycmVudF9yb3cgUkVDT1JEOwogICAgb2xkX3JvdyBqc29uOwogICAgbmV3X3JvdyBqc29uOwpCRUdJTgogICAgSUYgVEdfT1AgSU4gKCdJTlNFUlQnLCAnVVBEQVRFJykgVEhFTgogICAgICAgIGN1cnJlbnRfcm93IDo9IE5FVzsKICAgICAgICBuZXdfcm93IDo9IHJvd190b19qc29uKE5FVyk7CiAgICBFTFNFCiAgICAgICAgY3VycmVudF9yb3cgOj0gT0xEOwogICAgICAgIG5ld19yb3cgOj0gJ3t9Jzo6anNvbjsKICAgIEVORCBJRjsKICAgIElGIFRHX09QID0gJ0lOU0VSVCcgVEhFTgogICAgICAgIG9sZF9yb3cgOj0gJ3t9Jzo6anNvbjsKICAgIEVMU0VJRiBUR19PUCBJTiAoJ1VQREFURScsICdERUxFVEUnKSBUSEVOCiAgICAgICAgb2xkX3JvdyA6PSByb3dfdG9fanNvbihPTEQpOwogICAgRU5EIElGOwogICAgUEVSRk9STSBwZ19ub3RpZnkoCiAgICAgICAgJ3Jvd19jaGFuZ2VzJywKICAgICAgICBqc29uX2J1aWxkX29iamVjdCgKICAgICAgICAgICAgJ3RpbWVzdGFtcCcsIE5PVygpLAogICAgICAgICAgICAndXVpZCcsIHV1aWRfZ2VuZXJhdGVfdjQoKSwKICAgICAgICAgICAgJ3RhYmxlJywgVEdfVEFCTEVfTkFNRSwKICAgICAgICAgICAgJ2FjdGlvbicsIFRHX09QLAogICAgICAgICAgICAnX2lkJywgY3VycmVudF9yb3cuX2lkLAogICAgICAgICAgICAndXBkYXRlZF9hdCcsIGN1cnJlbnRfcm93LnVwZGF0ZWRfYXQsCiAgICAgICAgICAgICdtc19sYWcnLCBleHRyYWN0KE1JTExJU0VDT05EUyBmcm9tIElOVEVSVkFMICcwODowMCcgKyBOT1coKSAtIGN1cnJlbnRfcm93LnVwZGF0ZWRfYXQpCiAgICAgICAgKTo6dGV4dAogICAgKTsKICAgIFJFVFVSTiBjdXJyZW50X3JvdzsKRU5EOwokJCBMQU5HVUFHRSBwbHBnc3FsOwoKQ1JFQVRFIFRSSUdHRVIgY2hhbmdlc190b19yb3dfdHJpZ2dlciBCRUZPUkUgaW5zZXJ0IG9yIHVwZGF0ZSBvciBkZWxldGUgT04gc2FtcGxlX2l0ZW1zCkZPUiBFQUNIIFJPVyBFWEVDVVRFIFBST0NFRFVSRSBub3RpZnlfcm93X2NoYW5nZXMoKTsKYGBgCgojIyBtc19sYWcgd2FzIGNhbGN1bGF0ZWQgYXMgZm9sbG93czoKCmBgYHNxbApleHRyYWN0KE1JTExJU0VDT05EUyBmcm9tIElOVEVSVkFMICcwODowMCcgKyBOT1coKSAtIHVwZGF0ZWRfYXQpIGFzIG1zX2xhZwpgYGAKCiMjIFRoZSB0cmlnZ2VyIG91dHB1dAoKTk9USUZZJ3Mgb3V0cHV0IHdhcyBvYnNlcnZlZCB1c2luZyBQb3N0Z3JlcydzIExJU1RFTiBmdW5jdGlvbmFsaXR5IGFuZCBhIHBheWxvYWQgd2FzIGRlbGl2ZXJlZCBpbiB0aGUgZm9sbG93aW5nIHNjaGVtYToKCmBgYGpzb24KewogICJ0aW1lc3RhbXAiOiAiMjAxNy0wMi0yNFQwMDowODoyMC45MTgzMSswMDowMCIsCiAgInV1aWQiOiAiZTI1ZWM3NTItN2RlMy00ZGUwLWI3NjQtYmRhYzlhZjFhMDViIiwKICAidGFibGUiOiAic2FtcGxlX2l0ZW1zIiwKICAiYWN0aW9uIjogIklOU0VSVCIsCiAgIl9pZCI6ICI0ZjYwYzQ1NDUyZTc1MjAwMDMwMDAwMTgiLAogICJ1cGRhdGVkX2F0IjogIjIwMTctMDItMjRUMDA6MDg6MjAuMzcxIiwKICAibXNfbGFnIjogNTQ3LjMxCn0KYGBgCgojIFJlc3VsdHMKCmBgYHtyfQpkZiA8LSBidWlsZF9kZigibXNfbGFnX2Zyb21fMjgwMDBfZW50cmllc19jYWxjdWxhdGVkX2J5X3RyaWdnZXIubG9nIikKcHJpbnRfcXVhbnRpbGUoZGYpCnByaW50X2hpc3RvZ3JhbShkZiwgIkhpc3RvZ3JhbSBvZiBtc19sYWcgdnMgY291bnQiLCAibXNfbGFnIiwgImNvdW50IikKYGBgCgojIENvbmNsdXNpb24KCkxvb2tpbmcgYXQgdGhlIFF1YW50aWxlIHBlcmNlbnRhZ2VzIHdoZW4gdGVzdGluZyBvdXQgTW9yZVNRTCwgNTAlIG9mIHJlcXVlc3RzIGNvbWUgdGhyb3VnaCB3aXRoIDUyOW1zIG9yIGxlc3Mgb2YgbGFnLiA5MCUgb2YgcmVxdWVzdHMgd2VyZSBwcm9jZXNzZWQgaW4gdHJpZ2dlciB3aXRoIDEwNjNtcyBvZiBsYWcgb3IgbGVzcy4gMTAwJSBvZiByZXF1ZXN0cyBjYW1lIHRocm91Z2ggaW4gMTUwMG1zIG9mIGxhZyBvciBsZXNzLg==