{"record":{"id":"67065c7c767297e8","repo":"apache/pulsar","slug":"per-instance-cpu-requested-s-for-function-shoul","errorCode":null,"errorMessage":"Per instance cpu requested, %s, for function should be positive and a multiple of the granularity, %s","messagePattern":"Per instance cpu requested, (.+?), for function should be positive and a multiple of the granularity, (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/runtime/src/main/java/org/apache/pulsar/functions/runtime/kubernetes/KubernetesRuntimeFactory.java","lineNumber":508,"sourceCode":"                                \"Per instance RAM requested, %s, for function is greater than the maximum required, %s\",\n                                functionDetails.getResources().getRam(), maxRam));\n            }\n        }\n    }\n\n    void validateResourcesGranularityAndProportion(FunctionDetails functionDetails) {\n        final long baseMillis = 1000;\n        long multiples = 0L;\n        if (functionInstanceResourceGranularities != null) {\n            Double grnCpu = functionInstanceResourceGranularities.getCpu();\n            Long grnRam = functionInstanceResourceGranularities.getRam();\n            if (grnCpu != null) {\n                // convert cpus to milli-cores to avoid loss of precision\n                long grnCpuMillis = Math.round(baseMillis * grnCpu);\n                if (grnCpuMillis > 0) {\n                    long cpuMillis = Math.round(baseMillis * functionDetails.getResources().getCpu());\n                    if (cpuMillis == 0 || cpuMillis % grnCpuMillis != 0) {\n                        throw new IllegalArgumentException(\n                                String.format(\"Per instance cpu requested, %s, for function should be positive and a \"\n                                                + \"multiple of the granularity, %s\",\n                                        functionDetails.getResources().getCpu(), grnCpu));\n                    }\n                    if (functionInstanceResourceChangeInLockStep) {\n                        multiples = cpuMillis / grnCpuMillis;\n                    }\n                }\n            }\n            if (grnRam != null && grnRam > 0) {\n                if (functionDetails.getResources().getRam() == 0\n                        || functionDetails.getResources().getRam() % grnRam != 0) {\n                    throw new IllegalArgumentException(\n                            String.format(\"Per instance ram requested, %s, \"\n                                            + \"for function should be positive and a multiple of the granularity, %s\",\n                                    functionDetails.getResources().getRam(), grnRam));\n                }\n                if (functionInstanceResourceChangeInLockStep && multiples > 0) {","sourceCodeStart":490,"sourceCodeEnd":526,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/runtime/src/main/java/org/apache/pulsar/functions/runtime/kubernetes/KubernetesRuntimeFactory.java#L490-L526","documentation":"KubernetesRuntimeFactory.validateResourcesGranularityAndProportion enforces that a function's requested CPU is positive and an exact multiple of the configured CPU granularity (functionInstanceResourceGranularity). The check converts both values to milli-cores to avoid floating-point loss of precision; if cpuMillis is 0 or cpuMillis % grnCpuMillis != 0, admission is rejected with this IllegalArgumentException during doAdmissionChecks.","triggerScenarios":"Submitting/updating a function (doAdmissionChecks) to a Kubernetes runtime where pulsar.functions.functionInstanceResourceGranularity sets a CPU granularity and FunctionDetails.getResources().getCpu() is 0, negative, or not an exact multiple of that granularity.","commonSituations":"Tenant submits cpu=0.35 with granularity 0.5; resources left unset so cpu=0 while a granularity is configured; CPU set in cores while granularity is expressed in fractional values whose milli-core rounding makes the modulo fail (e.g. 0.1*1000 rounding issues); migrating functions from a cluster without granularity limits to one with them.","solutions":["Set the function's resources.cpu to a positive value that is an exact multiple of the configured CPU granularity (e.g. granularity 0.5 -> use 0.5, 1.0, 1.5).","If no granularity constraint is desired, unset/disable functionInstanceResourceGranularity in the worker config instead of fighting the modulo check.","If resources were omitted, explicitly configure resources (cpu and ram) on the function manifest before submission.","If functionInstanceResourceChangeInLockStep is enabled, also ensure the cpu multiple equals the ram multiple to avoid the follow-on error 1342."],"exampleFix":"// before (granularity = 0.5)\nresources:\n  cpu: 0.35\n// after\nresources:\n  cpu: 0.5","handlingStrategy":"validation","validationCode":"long granularityMillis = Math.round(1000 * configuredCpuGranularity); // e.g. 0.5 -> 500\nlong cpuMillis = Math.round(1000 * functionDetails.getResources().getCpu());\nif (cpuMillis <= 0 || cpuMillis % granularityMillis != 0) {\n    throw new IllegalArgumentException(\"cpu must be positive and a multiple of \" + configuredCpuGranularity);\n}","typeGuard":"boolean isValidCpu(Double cpu, double granularity) {\n    if (cpu == null || granularity <= 0) return false;\n    long cpuMillis = Math.round(1000 * cpu);\n    long grnMillis = Math.round(1000 * granularity);\n    return cpuMillis > 0 && cpuMillis % grnMillis == 0;\n}","tryCatchPattern":null,"preventionTips":["Read the worker's functionInstanceResourceGranularity setting before sizing function resources.","Always set both cpu and ram explicitly on function manifests.","Keep cpu/ram values on granularity boundaries to survive lock-step checks."],"tags":["pulsar-functions","kubernetes","resource-validation","configuration"],"backgroundTag":"resource-granularity-violation","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}