您可以尝试考虑使用利用 Couchbase 事件的纯 KV 解决方案。我想您会发现,这种实时解决方案本质上是对 lambda 或触发器进行编码,在数以百万计的文档中表现非常出色。为此,我将在下面给出一个具体的例子:
您似乎有两种类型的文件
首先,这里的销售代表授权列表本质上是您作为 JSON 文档的数据
KEY auth:1003
{
"Products": [
{
"EndDt": "9999-12-25",
"ProductId": 1,
"StartDt": "2022-05-15"
},
{
"EndDt": "9999-12-25",
"ProductId": 2,
"StartDt": "2022-05-15"
},
{
"EndDt": "9999-12-25",
"ProductId": 8,
"StartDt": "2022-05-15"
},
{
"EndDt": "9999-12-25",
"ProductId": 9,
"StartDt": "2022-05-15"
}
],
"id": 1003,
"type": "auth"
}
KEY auth:1002
{
"Products": [
{
"EndDt": "9999-12-25",
"ProductId": 1,
"StartDt": "2022-05-15"
},
{
"EndDt": "9999-12-25",
"ProductId": 2,
"StartDt": "2022-05-15"
},
{
"EndDt": "9999-12-25",
"ProductId": 5,
"StartDt": "2022-05-15"
},
{
"EndDt": "9999-12-25",
"ProductId": 6,
"StartDt": "2022-05-15"
}
],
"id": 1002,
"type": "auth"
}
KEY auth:1001
{
"Products": [
{
"EndDt": "9999-12-25",
"ProductId": 1,
"StartDt": "2022-05-15"
},
{
"EndDt": "9999-12-25",
"ProductId": 2,
"StartDt": "2022-05-15"
},
{
"EndDt": "9999-12-25",
"ProductId": 3,
"StartDt": "2022-05-15"
},
{
"EndDt": "9999-12-25",
"ProductId": 4,
"StartDt": "2022-05-15"
}
],
"id": 1001,
"type": "auth"
}
其次,您要在这里验证的一堆订单本质上是您作为 JSON 文档的数据(我冒昧地添加了一个以获得成功)
KEY: order:1234
{
"ProductIds": [
1,2,3,4,5
],
"RepID": 1001,
"id": 1234,
"type": "order"
}
KEY: order:1111
{
"ProductIds": [
1,2,3,4
],
"RepID": 1003,
"id": 1111,
"type": "order"
}
KEY: order:2222
{
"ProductIds": [
8,9
],
"RepID": 1003,
"id": 2222,
"type": "order"
}
KEY: order:100
{
"ProductIds": [
1,2,3
],
"RepID": 1002,
"id": 100,
"type": "order"
}
现在这是一个事件函数(它将在 6.X 和 7.X 模式下运行,尽管如果您利用存储桶支持的缓存,7.X 会快得多)
// Need two buckets (if 7.0+ keyspaces of _default._default)
// "eventing"
// "data"
// Need one bucket binding
// alias = src_col bucket = data mode = r+w
// For performance set workers to 2X VCPUs for large data sets
// or for very fast mutation rates.
function OnUpdate(doc, meta) {
// only process and validate orders (might add more filters here).
if (doc.type !== "order") return;
// level 1 is what you want, else to look at issue just raise the #
var DEBUG = 1;
// Use bucket backed caching to speed up loading of check document by 25X
var VERSION_AT_702 = false;
if (DEBUG > 1) log("checking order", meta.id);
// load the rep's authorized products fromthe bucket binding.
var auths;
if (VERSION_AT_702 == false) {
auths = src_col["auth:" + doc.RepID];
} else {
// use bucket backed caching. Will only read KV at most once per
// second per each Eventing node. Costs just 1/25th of a std. Bucket Op.
var result = couchbase.get(src_col,{"id": "auth:" + doc.RepID}, {"cache": true});
if (!result.success) {
auths = null;
} else {
auths = result.doc;
}
}
if (!auths) {
if (DEBUG > 0) log("no auth record found for RepID", doc.RepID);
return;
}
if (DEBUG > 4) log(auths);
// since I save the lists this isn't an optimal check
var is_authed = [];
var is_not_authed = [];
// now make sure the rep is authorized to sell all products
for (var k = 0; k < doc.ProductIds.length; k++){
var prod = doc.ProductIds[k];
if (DEBUG > 1) log("checking product",prod);
var okay = false;
for (var j = 0; j < auths.Products.length; j++){
var auth = auths.Products[j];
if (DEBUG > 6) log("\t1.",auth);
if (auth.ProductId == prod) {
if (DEBUG > 8) log("\t\t2.",auth.ProductId," === ", prod, "GOOD");
okay = true;
} else {
if (DEBUG > 8) log("\t\t2.",auth.ProductId," === ", prod, "BAD");
}
}
if (okay === false) {
is_not_authed.push(prod);
} else {
is_authed.push(prod);
}
if (DEBUG > 5) log("prod",prod,"authed",okay);
}
// =====================================================
// we have an issue id is_not_authed.length > 0
//======================================================
if (is_not_authed.length > 0) {
if (DEBUG > 0) log("BAD illegal order", meta.id, "rep", doc.RepID, "can sell products", is_authed, "but can't sell products", is_not_authed);
} else {
if (DEBUG > 0) log("VALID legal order", meta.id, "rep", doc.RepID, "can sell products", is_authed);
}
// =====================================================
// we could move the document or modify it but that's
// you business logic. Typically we might do something like:
// 1. update the document with a new tag.
// doc.verify_status = (is_not_authed.length == 0)
// src_col[meta.id] = doc;
// 2. at the top of the Function add another filter to
// prevent redoing the same work.
// if (doc.verify_status) return;
//======================================================
}
针对上述数据运行上述事件函数,我得到以下日志消息。
2022-08-03T19:14:50.936+00:00 [INFO] "BAD illegal order" "order:1111" "rep" 1003 "can sell products" [1,2] "but can't sell products" [3,4]
2022-08-03T19:14:50.848+00:00 [INFO] "BAD illegal order" "order:100" "rep" 1002 "can sell products" [1,2] "but can't sell products" [3]
2022-08-03T19:14:50.812+00:00 [INFO] "VALID legal order" "order:2222" "rep" 1003 "can sell products" [8,9]
2022-08-03T19:14:50.797+00:00 [INFO] "BAD illegal order" "order:1234" "rep" 1001 "can sell products" [1,2,3,4] "but can't sell products" [5]
当然,除了记录消息之外,您还想做一些事情,也许您想移动文档,添加或更新文档中的属性,或者在您使用纯 JavaScript 并通过 KV(或数据服务)访问您的Couchbase 中的数据。
请注意,在上面的代码中,我保留了哪些“可以”和“不能”出售的列表,但是如果您不需要,您可以通过中断优化循环(JavaScript v8 很快)但我确实看到了您的规模效率是关键。
也许将 Products 分成三个数组,然后您可以执行以下操作:
KEY auth:1001
{
"id": 1001,
"type": "auth",
"Product": [ 1, 2, 3, 4 ],
"StartDt": [ "2022-05-15", "2022-05-15", "2022-05-15", "2022-05-15" ],
"StartDt": [ "9999-12-25", "9999-05-15", "9999-12-25", "9999-12-25" ]
}
消除for循环:
const includesAll = (arr, values) => values.every(v => arr.includes(v));
log(meta.id,includesAll(auths.Product, doc.ProductIds));
如果交集“工作”太长,请查看 FastBitSet.js 之类的内容以缩短分析时间。
提高性能的最简单方法是启用存储桶支持的缓存(需要 7.0.2 或更高版本),但是如果您没有重用,这将无济于事。顺便说一句,发出日志消息也会减慢速度,因此请避免这种情况。
恕我直言,您应该能够在小型集群上处理 100K 文档/秒,在大型调谐集群上处理高达 1M 文档/秒。
如果您不熟悉 Eventing Service,您应该先运行几个 "step by step" examples 以获得基本了解。
如果由于某种原因您需要更高的性能(我认为您不会),我可以分享一些高级的三项赛技巧来加快速度,即使没有 - 只需 DM 我,我们会安排一些时间来谈谈。