Skip to content

Commit 9370ce9

Browse files
authored
Merge pull request #77 from statsig-io/fix-user-bucket-condition
Fix user bucket condition
2 parents d16f453 + e143df5 commit 9370ce9

File tree

4 files changed

+21
-13
lines changed

4 files changed

+21
-13
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "statsig-node",
3-
"version": "4.0.1",
3+
"version": "4.1.0",
44
"description": "Statsig Node.js SDK for usage in multi-user server environments.",
55
"main": "src/index.js",
66
"scripts": {

src/Evaluator.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ const SpecStore = require('./SpecStore');
1111
const UAParser = require('ua-parser-js');
1212

1313
const TYPE_DYNAMIC_CONFIG = 'dynamic_config';
14+
const CONDITION_SEGMENT_COUNT = 10 * 1000;
15+
const USER_BUCKET_COUNT = 1000;
1416

1517
const Evaluator = {
1618
async init(options, secretKey) {
@@ -74,10 +76,12 @@ const Evaluator = {
7476
},
7577

7678
_evalPassPercent(user, rule, salt) {
77-
const bucket = computeUserHashBucket(
79+
const hash = computeUserHash(
7880
salt + '.' + rule.name + '.' + user?.userID ?? '',
7981
);
80-
return bucket < rule.passPercentage * 100;
82+
return (
83+
Number(hash % BigInt(CONDITION_SEGMENT_COUNT)) < rule.passPercentage * 100
84+
);
8185
},
8286

8387
/**
@@ -141,7 +145,8 @@ const Evaluator = {
141145
break;
142146
case 'user_bucket':
143147
const salt = condition.additionalValues?.salt;
144-
value = computeUserHashBucket(salt + '.' + user?.userID ?? '');
148+
const userHash = computeUserHash(salt + '.' + user?.userID ?? '');
149+
value = Number(userHash % BigInt(USER_BUCKET_COUNT));
145150
break;
146151
default:
147152
return FETCH_FROM_SERVER;
@@ -276,13 +281,12 @@ const Evaluator = {
276281
},
277282
};
278283

279-
function computeUserHashBucket(userHash) {
280-
const hash = crypto
284+
function computeUserHash(userHash) {
285+
return crypto
281286
.createHash('sha256')
282287
.update(userHash)
283288
.digest()
284289
.readBigUInt64BE();
285-
return Number(hash % BigInt(10000));
286290
}
287291

288292
function getFromUser(user, field) {

src/__tests__/Evaluator.test.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,14 @@ describe('Test condition evaluation', () => {
189189
['current_time', 'on', Date.now() - 24 * 3600 * 1000, null, user, false],
190190

191191
// user bucket
192-
['user_bucket', 'lt', 1000, null, { userID: 1}, false, { salt:'himalayan salt' }],
193-
['user_bucket', 'lt', 1000, null, { userID: 18}, true, { salt:'himalayan salt' }],
194-
['user_bucket', 'gt', 8800, null, { userID: 1}, true, { salt:'himalayan salt' }],
195-
['user_bucket', 'gt', 8800, null, { userID: 18}, false, { salt:'himalayan salt' }],
192+
['user_bucket', 'lt', 981, null, { userID: 1}, true, { salt:'himalayan salt' }],
193+
['user_bucket', 'lt', 229, null, { userID: 18}, true, { salt:'himalayan salt' }],
194+
['user_bucket', 'gt', 980, null, { userID: 1}, false, { salt:'himalayan salt' }],
195+
['user_bucket', 'gt', 229, null, { userID: 18}, false, { salt:'himalayan salt' }],
196+
['user_bucket', 'any', [228, 333, 555],null, { userID: 18}, true, { salt:'himalayan salt' }],
197+
['user_bucket', 'any', [229, 333, 555],null, { userID: 18}, false, { salt:'himalayan salt' }],
198+
['user_bucket', 'none', [229, 333, 555],null, { userID: 18}, true, { salt:'himalayan salt' }],
199+
// ['user_bucket', '', []]
196200

197201
// some random type not implemented yet
198202
['derived_field', 'eq', '0.25', 'd1_retention', user, FETCH_FROM_SERVER],

0 commit comments

Comments
 (0)