{"record":{"id":"085f48b30fe40e9e","repo":"apache/cassandra","slug":"the-next-training-or-importing-can-occur-only-at-l","errorCode":null,"errorMessage":"The next training or importing can occur only at least after %s from the last training which happened at %s. You can train again no earlier than at %s.","messagePattern":"The next training or importing can occur only at least after (.+?) from the last training which happened at (.+?)\\. You can train again no earlier than at (.+?)\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"warning","filePath":"src/java/org/apache/cassandra/db/compression/CompressionDictionaryManager.java","lineNumber":356,"sourceCode":"               .minTrainingFrequency(CompressionDictionaryTrainingConfig.getMinTrainingFrequency(compressionParams.getOtherOptions()))\n               .chunkSize(compressionParams.chunkLength())\n               .build();\n    }\n\n\n    private void checkTrainingFrequency(LightweightCompressionDictionary lastDictionary, CompressionDictionaryTrainingConfig config)\n    {\n        Instant lastTraining = lastDictionary == null ? null : lastDictionary.createdAt;\n\n        // if there is no dictionary trained so far or min frequency is 0 - that is we can train as often as we want -\n        // then do not check if we can\n        if (lastTraining != null && config.minTrainingFrequency != 0)\n        {\n            Instant now = FBUtilities.now();\n            if (lastTraining.isAfter(now.minus(config.minTrainingFrequency, ChronoUnit.MINUTES)))\n            {\n                Instant nextEarliestTraining = lastTraining.plus(config.minTrainingFrequency, ChronoUnit.MINUTES);\n                throw new IllegalArgumentException(format(\"The next training or importing can occur only at least after %s from the last training which happened at %s. \" +\n                                                          \"You can train again no earlier than at %s.\",\n                                                          new DurationSpec.IntMinutesBound(config.minTrainingFrequency, TimeUnit.MINUTES),\n                                                          lastTraining,\n                                                          nextEarliestTraining));\n            }\n        }\n    }\n\n    private void storeDictionary(CompressionDictionary dictionary)\n    {\n        if (!isEnabled)\n        {\n            return;\n        }\n\n        SystemDistributedKeyspace.storeCompressionDictionary(keyspaceName, tableName, tableId, dictionary);\n        cache.add(dictionary);\n    }","sourceCodeStart":338,"sourceCodeEnd":374,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/db/compression/CompressionDictionaryManager.java#L338-L374","documentation":"To avoid excessive training churn, the manager enforces a minimum interval (config.minTrainingFrequency, in minutes) between training or importing operations for a table. checkTrainingFrequency compares the last training timestamp against now and throws IllegalArgumentException when the request comes too soon, reporting when the next training is allowed.","triggerScenarios":"Calling train() or importCompressionDictionary() sooner than minTrainingFrequency minutes after the table's last recorded training, when minTrainingFrequency != 0 and a last-training timestamp exists.","commonSituations":"Automation scripts retrying training in a tight loop; multiple operators triggering training back-to-back; re-running an import right after training; setting a large minTrainingFrequency and forgetting it when testing.","solutions":["Wait until the reported nextEarliestTraining time before invoking train() or importCompressionDictionary() again.","Set minTrainingFrequency to 0 in the training parameters to disable the frequency check when appropriate (e.g. tests).","Make automation idempotent: record last-training time and only fire when the interval has elapsed.","Use force=true semantics carefully — the frequency check still applies via checkTrainingFrequency."],"exampleFix":"// before\nmanager.train(true, params); // every minute — throws\n// after\nMap<String, String> params = Map.of(\"min_training_frequency\", \"0\"); // or schedule after the allowed interval\nmanager.train(true, params);","handlingStrategy":"validation","validationCode":"Instant lastTraining = manager.getLastTrainingTime();\nDuration minFreq = Duration.ofMinutes(manager.getMinTrainingFrequency());\nif (lastTraining == null || lastTraining.isBefore(Instant.now().minus(minFreq))) manager.train(force, params);","typeGuard":null,"tryCatchPattern":"try { manager.train(force, params); } catch (IllegalArgumentException e) { /* too soon: parse nextEarliestTraining and schedule retry */ }","preventionTips":["Record last-training timestamps in automation and respect the interval","Back off and retry after the reported earliest time","Set min_training_frequency to 0 only for tests"],"tags":["compression","training","rate-limit"],"backgroundTag":"rate-limit-exceeded","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}