mongoose-sort-encrypted-field
Advanced tools
Comparing version 0.3.2 to 0.4.0
@@ -6,3 +6,3 @@ "use strict"; | ||
const REDIS_QUEUE_CLIENT_OPTIONS = { | ||
redis: new Redis(), | ||
redis: null, | ||
batchSize: 10, | ||
@@ -20,4 +20,4 @@ groupVisibilityTimeoutMs: 60000, | ||
selectSortFields: false, | ||
noOfCharsForSortId: 50, | ||
noOfCharsToIncreaseOnSaturation: 2, | ||
noOfBytesForSortId: 50, | ||
noOfBytesToIncreaseOnSaturation: 2, | ||
revaluateAllThreshold: 0.5, | ||
@@ -24,0 +24,0 @@ revaluateAllCountThreshold: 100, |
@@ -1,5 +0,4 @@ | ||
declare const mongoose: any; | ||
declare const PLUGIN_OPTIONS: any; | ||
declare const getModelsQueue: any; | ||
declare function sortEncryptedFields(schema: Schema, pluginOptions: PluginOptions): void; | ||
declare function getModelWithSortEncryptedFieldsPlugin(documentName: any, schema: any, pluginOptions: any): any; | ||
import mongoose from "mongoose"; | ||
declare function getModelWithSortEncryptedFieldsPlugin(documentName: any, schema: any, pluginOptions: any): mongoose.Model<any, unknown, unknown, unknown, any, any>; | ||
export default getModelWithSortEncryptedFieldsPlugin; | ||
export { getModelWithSortEncryptedFieldsPlugin }; |
@@ -0,8 +1,14 @@ | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getModelWithSortEncryptedFieldsPlugin = void 0; | ||
// mongoose is not in package.json to avoid compatibility issues with npm package user | ||
const mongoose = require("mongoose"); | ||
const { PLUGIN_OPTIONS } = require("./constants"); | ||
const { getModelsQueue } = require("./modelsQueue"); | ||
const mongoose_1 = __importDefault(require("mongoose")); | ||
const constants_1 = require("./constants"); | ||
const modelsQueue_1 = require("./modelsQueue"); | ||
function sortEncryptedFields(schema, pluginOptions) { | ||
const sortEncryptedFieldsOptions = { | ||
...PLUGIN_OPTIONS, | ||
...constants_1.PLUGIN_OPTIONS, | ||
...pluginOptions, | ||
@@ -22,3 +28,3 @@ }; | ||
[field.options.sortFieldName]: { | ||
type: String, | ||
type: Buffer, | ||
default: null, | ||
@@ -31,8 +37,8 @@ select: selectSortFields, | ||
sortEncryptedFieldsOptions.decrypters = decrypters; | ||
const modelsQueue = getModelsQueue(redisQueueClientOptions); | ||
const modelsQueue = (0, modelsQueue_1.getModelsQueue)(redisQueueClientOptions); | ||
sortEncryptedFieldsOptions.modelsQueue = modelsQueue; | ||
schema.options.sortEncryptedFieldsOptions = sortEncryptedFieldsOptions; | ||
schema["options"].sortEncryptedFieldsOptions = sortEncryptedFieldsOptions; | ||
schema.post("save", async function save(doc, next) { | ||
for (const [fieldName, sortFieldName] of Object.entries(sortFields)) { | ||
await modelsQueue.addJob(`${this.constructor.modelName}::${fieldName}::${sortFieldName}`, { | ||
await modelsQueue.addJob(`${this.constructor["modelName"]}::${fieldName}::${sortFieldName}`, { | ||
objectId: doc._id, | ||
@@ -48,8 +54,8 @@ fieldValue: doc[fieldName], | ||
const sortFieldName = sortFields[fieldName]; | ||
if (update.$set && update.$set[sortFieldName]) { | ||
if (update["$set"] && update["$set"][sortFieldName]) { | ||
// Bypass middleware internal call for updating any sortFieldName field | ||
break; | ||
} | ||
if (update.$set && update.$set[fieldName]) { | ||
update.$set[sortFieldName] = null; | ||
if (update["$set"] && update["$set"][fieldName]) { | ||
update["$set"][sortFieldName] = null; | ||
} | ||
@@ -63,7 +69,7 @@ } | ||
const sortFieldName = sortFields[fieldName]; | ||
if (update.$set && update.$set[sortFieldName]) { | ||
if (update["$set"] && update["$set"][sortFieldName]) { | ||
// Bypass middleware internal call for updating any sortFieldName field | ||
break; | ||
} | ||
if (update.$set && update.$set[fieldName]) { | ||
if (update["$set"] && update["$set"][fieldName]) { | ||
const document = await this.model.findOne(this.getFilter(), { _id: 1, [fieldName]: 1 }).exec(); | ||
@@ -85,8 +91,8 @@ if (document) { | ||
const sortFieldName = sortFields[fieldName]; | ||
if (update.$set && update.$set[sortFieldName]) { | ||
if (update["$set"] && update["$set"][sortFieldName]) { | ||
// Bypass middleware internal call for updating any sortFieldName field | ||
break; | ||
} | ||
if (update.$set && update.$set[fieldName]) { | ||
update.$set[sortFieldName] = null; | ||
if (update["$set"] && update["$set"][fieldName]) { | ||
update["$set"][sortFieldName] = null; | ||
} | ||
@@ -100,8 +106,8 @@ } | ||
const sortFieldName = sortFields[fieldName]; | ||
if (update.$set && update.$set[sortFieldName]) { | ||
if (update["$set"] && update["$set"][sortFieldName]) { | ||
// Bypass middleware internal call for updating any sortFieldName field | ||
break; | ||
} | ||
if (update.$set && update.$set[fieldName]) { | ||
const fieldValue = update.$set[fieldName]; | ||
if (update["$set"] && update["$set"][fieldName]) { | ||
const fieldValue = update["$set"][fieldName]; | ||
const documents = await this.model.find(this.getFilter(), { _id: 1 }).exec(); | ||
@@ -124,3 +130,3 @@ if (documents && documents.length > 0) { | ||
const { sortFields, modelsQueue, revaluateAllThreshold, revaluateAllCountThreshold } = schema.options.sortEncryptedFieldsOptions; | ||
const model = mongoose.model(documentName, schema); | ||
const model = mongoose_1.default.model(documentName, schema); | ||
for (const fieldName in sortFields) { | ||
@@ -165,2 +171,3 @@ modelsQueue.registerGroup(model, fieldName, sortFields[fieldName]); | ||
} | ||
module.exports = { getModelWithSortEncryptedFieldsPlugin }; | ||
exports.getModelWithSortEncryptedFieldsPlugin = getModelWithSortEncryptedFieldsPlugin; | ||
exports.default = getModelWithSortEncryptedFieldsPlugin; |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getModelsQueue = void 0; | ||
const { RedisQueueClient } = require("redis-ordered-queue"); | ||
const Redis = require("ioredis"); | ||
const { REDIS_QUEUE_CLIENT_OPTIONS } = require("./constants"); | ||
const { SortIdManager } = require("./sortIdManager"); | ||
const redis_ordered_queue_1 = require("redis-ordered-queue"); | ||
const ioredis_1 = __importDefault(require("ioredis")); | ||
const constants_1 = require("./constants"); | ||
const sortIdManager_1 = require("./sortIdManager"); | ||
const defaultRedisKeyPrefix = "mongoose-sort-encrypted-field"; | ||
let modelsQueue; | ||
async function handleMessage({ data, context: { lock: { groupId }, }, }) { | ||
const sortIdManger = modelsQueue.groupIdToSortIdManagerMap[groupId]; | ||
if (!sortIdManger) { | ||
console.log(`mongoose-sort-encrypted-field (Warining) -> Unable to find sortIdManager for groupId: ${groupId}, | ||
It might be some old job for that sort field is no longer exists | ||
Or you forgot to add sortFieldName option in schema field options.`); | ||
return; | ||
} | ||
if (!sortIdManger.silent) { | ||
const pendingJobsCount = await modelsQueue.getPendingJobsCount(); | ||
console.log(`mongoose-sort-encrypted-field -> handleMessage() -> noOfPendingJobs: ${pendingJobsCount}`); | ||
} | ||
if (data.updateSortIdForAllDocuments) { | ||
await sortIdManger.updateSortIdForAllDocuments(); | ||
return; | ||
} | ||
await sortIdManger.updateSortFieldsForDocument(data.objectId, data.fieldValue); | ||
} | ||
class ModelsQueue { | ||
@@ -14,40 +35,32 @@ client; | ||
groupIdToSortIdManagerMap = {}; | ||
redisClient; | ||
constructor(redisQueueClientOptions) { | ||
this.groupIdToSortIdManagerMap = {}; | ||
redisQueueClientOptions = { | ||
...REDIS_QUEUE_CLIENT_OPTIONS, | ||
...constants_1.REDIS_QUEUE_CLIENT_OPTIONS, | ||
...redisQueueClientOptions, | ||
}; | ||
const { redis } = redisQueueClientOptions; | ||
let redisClient = redis; | ||
if (!(redis instanceof Redis)) { | ||
redisClient = new Redis(redis); | ||
if (!redis) { | ||
this.redisClient = new ioredis_1.default(); | ||
} | ||
this.client = new RedisQueueClient({ | ||
else if (redis instanceof ioredis_1.default) { | ||
this.redisClient = redis; | ||
} | ||
else { | ||
this.redisClient = new ioredis_1.default(redis); | ||
} | ||
this.client = new redis_ordered_queue_1.RedisQueueClient({ | ||
...redisQueueClientOptions, | ||
redis: redisClient, | ||
redis: this.redisClient, | ||
}); | ||
this.client.startConsumers({ | ||
handleMessage: async function handleMessage({ data, context: { lock: { groupId }, }, }) { | ||
const sortIdManger = modelsQueue.groupIdToSortIdManagerMap[groupId]; | ||
if (!sortIdManger) { | ||
console.log(`mongoose-sort-encrypted-field (Warining) -> Unable to find sortIdManager for groupId: ${groupId}, | ||
It might be some old job for that sort field is no longer exists | ||
Or you forgot to add sortFieldName option in schema field options.`); | ||
return; | ||
} | ||
if (!sortIdManger.silent) { | ||
const noOfPendingJobs = (await modelsQueue.client.getMetrics(100)).topMessageGroupsMessageBacklogLength; | ||
console.log(`mongoose-sort-encrypted-field -> handleMessage() -> noOfPendingJobs: ${noOfPendingJobs}`); | ||
} | ||
if (data.updateSortIdForAllDocuments) { | ||
await sortIdManger.updateSortIdForAllDocuments(); | ||
return; | ||
} | ||
await sortIdManger.updateSortFieldsForDocument(data.objectId, data.fieldValue); | ||
}, | ||
}); | ||
this.client.startConsumers({ handleMessage }); | ||
} | ||
async getPendingJobsCount() { | ||
const metrics = await modelsQueue.client.getMetrics(100); | ||
const pendingJobsCount = metrics.topMessageGroupsMessageBacklogLength; | ||
return pendingJobsCount; | ||
} | ||
async addJob(groupId, data) { | ||
await this.client.send({ groupId, data }); | ||
await this.client.send({ groupId, data, priority: 1 }); | ||
} | ||
@@ -57,12 +70,12 @@ registerGroup(model, fieldName, sortFieldName) { | ||
if (!this.groupIdToSortIdManagerMap[groupId]) { | ||
this.groupIdToSortIdManagerMap[groupId] = new SortIdManager(model, fieldName, sortFieldName); | ||
this.groupIdToSortIdManagerMap[groupId] = new sortIdManager_1.SortIdManager(model, fieldName, sortFieldName); | ||
} | ||
} | ||
async removeAllJobs(modelName) { | ||
const keys = await this.client.redis.keys(`${defaultRedisKeyPrefix}::msg-group-queue::${modelName}`); | ||
var pipeline = this.client.redis.pipeline(); | ||
const keys = await this.redisClient.keys(`${defaultRedisKeyPrefix}::msg-group-queue::${modelName}`); | ||
var pipeline = this.redisClient.pipeline(); | ||
keys.forEach(function (key) { | ||
pipeline.del(key); | ||
}); | ||
pipeline.exec(); | ||
await pipeline.exec(); | ||
} | ||
@@ -69,0 +82,0 @@ } |
@@ -0,1 +1,2 @@ | ||
/// <reference types="node" /> | ||
declare class SortIdManager { | ||
@@ -7,7 +8,7 @@ model: any; | ||
ignoreCases: boolean; | ||
noOfCharsForSortId: number; | ||
noOfCharsToIncreaseOnSaturation: number; | ||
noOfBytesForSortId: number; | ||
noOfBytesToIncreaseOnSaturation: number; | ||
constructor(model: any, fieldName: any, sortFieldName: any); | ||
getDocument(skip: any): Promise<any>; | ||
getAverageSortId(predecessorSortId: any, successorSortId: any): any; | ||
getAverageSortId(predecessorSortId: Buffer, successorSortId: Buffer): any; | ||
generateSortIdUsingBinarySearch(fieldValue: any): Promise<any>; | ||
@@ -14,0 +15,0 @@ updateSortFieldsForDocument(objectId: any, fieldValue: any): Promise<void>; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.SortIdManager = void 0; | ||
const Base2N = require("@navpreetdevpuri/base-2-n"); | ||
const { add, shift } = require("math-buffer"); | ||
class SortIdManager { | ||
@@ -11,4 +11,4 @@ model; | ||
ignoreCases; | ||
noOfCharsForSortId; | ||
noOfCharsToIncreaseOnSaturation; | ||
noOfBytesForSortId; | ||
noOfBytesToIncreaseOnSaturation; | ||
constructor(model, fieldName, sortFieldName) { | ||
@@ -18,7 +18,7 @@ this.model = model; | ||
this.sortFieldName = sortFieldName; | ||
const { silent, ignoreCases, noOfCharsForSortId, noOfCharsToIncreaseOnSaturation } = model.schema.options.sortEncryptedFieldsOptions; | ||
const { silent, ignoreCases, noOfBytesForSortId, noOfBytesToIncreaseOnSaturation } = model.schema.options.sortEncryptedFieldsOptions; | ||
this.silent = silent; | ||
this.ignoreCases = ignoreCases; | ||
this.noOfCharsForSortId = noOfCharsForSortId; | ||
this.noOfCharsToIncreaseOnSaturation = noOfCharsToIncreaseOnSaturation; | ||
this.noOfBytesForSortId = noOfBytesForSortId; | ||
this.noOfBytesToIncreaseOnSaturation = noOfBytesToIncreaseOnSaturation; | ||
} | ||
@@ -34,25 +34,30 @@ async getDocument(skip) { | ||
if (!predecessorSortId) { | ||
predecessorSortId = "".padEnd(successorSortId.length, "\0"); | ||
predecessorSortId = Buffer.from("".padEnd(2 * successorSortId.length, "0"), "hex"); | ||
} | ||
if (!successorSortId) { | ||
successorSortId = "".padEnd(predecessorSortId.length, "\uffff"); | ||
successorSortId = Buffer.from("".padEnd(2 * predecessorSortId.length, "f"), "hex"); | ||
} | ||
let predecessorNumber; | ||
let successorNumber; | ||
predecessorSortId.reverse(); | ||
successorSortId.reverse(); | ||
if (predecessorSortId.length === successorSortId.length) { | ||
predecessorNumber = new Base2N(predecessorSortId, 16); | ||
successorNumber = new Base2N(successorSortId, 16); | ||
const averageNumber = predecessorNumber.average(successorNumber); | ||
if (averageNumber.toString() !== predecessorNumber.toString()) { | ||
return averageNumber.toString(); | ||
if (add(predecessorSortId, Buffer.from([0x01])).equals(successorSortId)) { | ||
predecessorSortId = Buffer.concat([Buffer.from("".padEnd(2 * this.noOfBytesToIncreaseOnSaturation, "0"), "hex"), predecessorSortId]); | ||
successorSortId = Buffer.concat([Buffer.from("".padEnd(2 * this.noOfBytesToIncreaseOnSaturation, "0"), "hex"), successorSortId]); | ||
} | ||
predecessorNumber = new Base2N(predecessorSortId.padEnd(averageNumber.length + this.noOfCharsToIncreaseOnSaturation, "\0"), 16); | ||
successorNumber = new Base2N(successorSortId.padEnd(averageNumber.length + this.noOfCharsToIncreaseOnSaturation, "\0"), 16); | ||
return predecessorNumber.average(successorNumber).toString(); | ||
let averageSortId = add(shift(predecessorSortId, -1), shift(successorSortId, -1)); | ||
if (averageSortId[averageSortId.length - 1] === 0 && averageSortId.length > predecessorSortId.length) { | ||
averageSortId = averageSortId.slice(0, averageSortId.length - 1); | ||
} | ||
averageSortId.reverse(); | ||
return averageSortId; | ||
} | ||
const bigger = predecessorSortId.length > successorSortId.length ? predecessorSortId : successorSortId; | ||
const smaller = successorSortId.length > predecessorSortId.length ? predecessorSortId : successorSortId; | ||
const biggerNumber = new Base2N(bigger, 16); | ||
const smallerNumber = new Base2N(smaller.padEnd(bigger.length, "\0"), 16); | ||
return biggerNumber.average(smallerNumber).toString(); | ||
const biggerSortId = predecessorSortId.length > successorSortId.length ? predecessorSortId : successorSortId; | ||
let smallerSortId = successorSortId.length > predecessorSortId.length ? predecessorSortId : successorSortId; | ||
smallerSortId = Buffer.concat([Buffer.from("".padEnd(2 * (biggerSortId.length - smallerSortId.length), "0"), "hex"), smallerSortId]); | ||
let averageSortId = add(shift(predecessorSortId, -1), shift(successorSortId, -1)); | ||
if (averageSortId[averageSortId.length - 1] === 0 && averageSortId.length > biggerSortId.length) { | ||
averageSortId = averageSortId.slice(0, averageSortId.length - 1); | ||
} | ||
averageSortId.reverse(); | ||
return averageSortId; | ||
} | ||
@@ -67,4 +72,4 @@ async generateSortIdUsingBinarySearch(fieldValue) { | ||
if (n === 0) { | ||
const predecessorSortId = new Base2N("\0", 16, this.noOfCharsForSortId).toString(); | ||
const successorSortId = "".padEnd(this.noOfCharsForSortId, "\uffff"); | ||
const predecessorSortId = Buffer.from("".padEnd(2 * this.noOfBytesForSortId, "0"), "hex"); | ||
const successorSortId = Buffer.from("".padEnd(2 * this.noOfBytesForSortId, "f"), "hex"); | ||
return this.getAverageSortId(predecessorSortId, successorSortId); | ||
@@ -122,3 +127,3 @@ } | ||
const newSortId = await this.generateSortIdUsingBinarySearch(fieldValue); | ||
await this.model.updateOne({ _id: objectId }, { $set: { [this.sortFieldName]: newSortId.toString() } }); | ||
await this.model.updateOne({ _id: objectId }, { $set: { [this.sortFieldName]: newSortId } }); | ||
if (!this.silent) { | ||
@@ -142,10 +147,10 @@ console.timeEnd(`mongoose-sort-encrypted-field -> updateSortFieldsForDocument() -> objectId: ${objectId}, fieldName: ${this.fieldName}, sortFieldName: ${this.sortFieldName}, timeTaken: `); | ||
const log2n = Math.round(Math.log2(n)) + 1; | ||
let diff = new Base2N("".padEnd(this.noOfCharsForSortId, "\uffff"), 16); | ||
let diff = Buffer.from("".padEnd(this.noOfBytesForSortId, "f"), "hex"); | ||
for (let i = 0; i < log2n; i += 1) { | ||
diff = diff.half(); | ||
diff = shift(diff, -1); | ||
} | ||
let curr = new Base2N("\0", 16, this.noOfCharsForSortId); | ||
let curr = Buffer.from("".padEnd(this.noOfBytesForSortId, "0"), "hex"); | ||
for (let i = 0; i < n; i += 1) { | ||
if (i === 0 || documents[i - 1][this.fieldName] !== documents[i][this.fieldName]) { | ||
curr = curr.add(diff); | ||
curr = add(curr, diff); | ||
} | ||
@@ -152,0 +157,0 @@ await this.model.updateOne({ _id: documents[i]._id }, { $set: { [this.sortFieldName]: curr.toString() } }); |
{ | ||
"name": "mongoose-sort-encrypted-field", | ||
"version": "0.3.2", | ||
"version": "0.4.0", | ||
"description": "Mongoose plugin to enable sorting on encrypted fields", | ||
"main": "lib/index.js", | ||
"scripts": { | ||
"build": "npx tsc" | ||
"build": "npx tsc", | ||
"test": "mocha --exit --timeout 300000" | ||
}, | ||
@@ -29,4 +30,4 @@ "repository": { | ||
"dependencies": { | ||
"@navpreetdevpuri/base-2-n": "0.0.9", | ||
"ioredis": "^5.3.1", | ||
"math-buffer": "^0.1.1", | ||
"redis-ordered-queue": "^1.4.2" | ||
@@ -36,4 +37,10 @@ }, | ||
"@types/node": "^18.15.3", | ||
"crypto": "^1.0.1", | ||
"mocha": "^10.2.0", | ||
"mongodb-memory-server": "^8.12.1", | ||
"mongoose": "^7.0.3", | ||
"project-name-generator": "^2.1.9", | ||
"redis-memory-server": "^0.6.0", | ||
"typescript": "^5.0.2" | ||
} | ||
} |
@@ -90,10 +90,10 @@ # mongoose-sort-encrypted-field | ||
2. `noOfCharsForSortId?: number` default: `50` | ||
Number of characters for sort ID, bigger number is mathematically better. | ||
2. `noOfBytesForSortId?: number` default: `50` | ||
Number of bytes for sort ID, bigger number is mathematically better. | ||
3. `noOfCharsToIncreaseOnSaturation?: number;` default: `2` <br> | ||
Number of chars to increase on saturation, for example, | ||
3. `noOfBytesToIncreaseOnSaturation?: number;` default: `2` <br> | ||
Number of bytes to increase on saturation, for example, | ||
for `04` and `05`, first, we can see there is no whole number between those | ||
so, It appends an extra digit at the end and it becomes `040` and `050` and the average is `045`. | ||
In the base `2^16` number system, getting a saturation like that is mathematically very unlikely. | ||
In the base `2^8` number system, getting a saturation like that is mathematically very unlikely. | ||
@@ -111,3 +111,3 @@ 4. `ignoreCases?: boolean;` default: `false` <br> | ||
If the number of documents without sort ID divided by the total number of documents is less than this threshold | ||
Then it will get all values, sort them, and generate sort ID for all at equal distances 0 to 2^16 | ||
Then it will get all values, sort them, and generate sort ID for all at equal distances 0 to 2^8 | ||
For example, if we have 3 documents and we can 00 to 20 sort ID | ||
@@ -122,6 +122,6 @@ then those documents will have 05 10 15 sort ID | ||
We create a sort order ID which is just a number in base `2^16`, which is a huge number system as compared to the 10 base number system. We search in DB using binary search. For `1 lakh` documents, it queries and decrypts only `18` documents (first+last+log(1lakh)) to generate a sort ID. It generates a sort order ID in `O(1)`. | ||
We create a sort order ID which is just a number in base `2^8`, which is a huge number system as compared to the 10 base number system. We search in DB using binary search. For `1 lakh` documents, it queries and decrypts only `18` documents (first+last+log(1lakh)) to generate a sort ID. It generates a sort order ID in `O(1)`. | ||
To generate a sort order ID it only needs to know the previous and next sort ID, and it just averages out those to get the current sort order ID, for example in the base 10 system if need to insert between `03` and `07` then `(03+07)/02` which is `05`. for `04` and `05`, first we can see there is no whole number between those so, It append extra digit at the end and it becomes `040` and `050` and the average is `045`. In the base `2^16` number system, getting a saturation like that is mathematically very unlikely. | ||
To generate a sort order ID it only needs to know the previous and next sort ID, and it just averages out those to get the current sort order ID, for example in the base 10 system if need to insert between `03` and `07` then `(03+07)/02` which is `05`. for `04` and `05`, first we can see there is no whole number between those so, It append extra digit at the end and it becomes `040` and `050` and the average is `045`. In the base `2^8` number system, getting a saturation like that is mathematically very unlikely. | ||
It uses [redis-ordered-queue](https://www.npmjs.com/package/redis-ordered-queue) to generate a sort ID. It means it only processes one document at a time as per the mathematical requirement of the sort ID generation algorithm even when we are running multiple instances of our service. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
63190
20
1814
8
1
1
+ Addedmath-buffer@^0.1.1
+ Addedmath-buffer@0.1.1(transitive)
- Removed@navpreetdevpuri/base-2-n@0.0.9
- Removed@navpreetdevpuri/base-2-n@0.0.9(transitive)