{"record":{"id":"327a40cc380acfd1","repo":"nathanmarz/storm","slug":"numbuckets-must-be-2-timecachemap","errorCode":null,"errorMessage":"numBuckets must be >= 2","messagePattern":"numBuckets must be >= 2","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"storm-core/src/jvm/backtype/storm/utils/TimeCacheMap.java","lineNumber":54,"sourceCode":"//deprecated in favor of non-threaded RotatingMap\n@Deprecated\npublic class TimeCacheMap<K, V> {\n    //this default ensures things expire at most 50% past the expiration time\n    private static final int DEFAULT_NUM_BUCKETS = 3;\n\n    public static interface ExpiredCallback<K, V> {\n        public void expire(K key, V val);\n    }\n\n    private LinkedList<HashMap<K, V>> _buckets;\n\n    private final Object _lock = new Object();\n    private Thread _cleaner;\n    private ExpiredCallback _callback;\n    \n    public TimeCacheMap(int expirationSecs, int numBuckets, ExpiredCallback<K, V> callback) {\n        if(numBuckets<2) {\n            throw new IllegalArgumentException(\"numBuckets must be >= 2\");\n        }\n        _buckets = new LinkedList<HashMap<K, V>>();\n        for(int i=0; i<numBuckets; i++) {\n            _buckets.add(new HashMap<K, V>());\n        }\n\n\n        _callback = callback;\n        final long expirationMillis = expirationSecs * 1000L;\n        final long sleepTime = expirationMillis / (numBuckets-1);\n        _cleaner = new Thread(new Runnable() {\n            public void run() {\n                try {\n                    while(true) {\n                        Map<K, V> dead = null;\n                        Time.sleep(sleepTime);\n                        synchronized(_lock) {\n                            dead = _buckets.removeLast();","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/nathanmarz/storm/blob/cdb116e942666973bc4eaa0df098d5bab82739e7/storm-core/src/jvm/backtype/storm/utils/TimeCacheMap.java#L36-L72","documentation":"The TimeCacheMap constructor validates numBuckets and throws IllegalArgumentException if fewer than 2 buckets are requested. With only one bucket the expired-bucket rotation scheme cannot work (the active bucket would be cleaned immediately or never rotate), so the class forbids it at construction time.","triggerScenarios":"Calling new TimeCacheMap<K,V>(expirationSecs, numBuckets, callback) — or the shorter constructor defaulting numBuckets — with numBuckets < 2, e.g. new TimeCacheMap(3, 1, cb) or new TimeCacheMap(3, 0, cb).","commonSituations":"Passing a user-supplied or config-derived bucket count that is 0 or 1; computing numBuckets from expirationSecs/bucketInterval arithmetic that rounds down to 1; misreading defaults in code copied from an older Storm version.","solutions":["Pass numBuckets >= 2, e.g. new TimeCacheMap(expirationSecs, 3, callback).","Use the convenience constructor new TimeCacheMap(expirationSecs, callback), which defaults numBuckets to 3.","Clamp computed values: int buckets = Math.max(2, computedBuckets); before constructing.","Validate the config value driving numBuckets at startup and fail with a clearer message."],"exampleFix":"// before\nTimeCacheMap<String, Long> cache = new TimeCacheMap<>(expiry, 1, cb);\n// after\nTimeCacheMap<String, Long> cache = new TimeCacheMap<>(expiry, Math.max(2, configuredBuckets), cb);","handlingStrategy":"validation","validationCode":"if (buckets < 2) throw new IllegalArgumentException(\"TimeCacheMap requires numBuckets >= 2, got \" + buckets);","typeGuard":null,"tryCatchPattern":"try {\n    cache = new TimeCacheMap<>(expirySecs, buckets, callback);\n} catch (IllegalArgumentException e) {\n    LOG.warn(\"Invalid bucket count, using default 3\");\n    cache = new TimeCacheMap<>(expirySecs, callback);\n}","preventionTips":["Always construct with the two-arg constructor unless you have a specific reason to tune numBuckets.","Clamp any config-derived bucket count with Math.max(2, value).","Unit-test cache construction with boundary values (0, 1, 2)."],"tags":["cache","constructor","argument-validation","storm"],"backgroundTag":"invalid-constructor-argument","analyzedSha":"cdb116e942666973bc4eaa0df098d5bab82739e7","analyzedAt":"2026-09-12T14:30:00.714Z","contentChangedAt":"2026-09-12T14:30:00.714Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}