@@ -208,55 +208,78 @@ private void validateRegistration(
208208 final String helpForValidation = help ;
209209 final Unit unitForValidation = unit ;
210210
211- // Check exposition name collisions before modifying any state.
212211 Set <String > expositionNames = computeExpositionNames (prometheusName , type );
213- for (String expositionName : expositionNames ) {
214- String owner = expositionNameOwners .get (expositionName );
215- if (owner != null && !owner .equals (prometheusName )) {
216- throw new IllegalArgumentException (
217- "'"
218- + prometheusName
219- + "' and '"
220- + owner
221- + "' have conflicting exposition name: '"
222- + expositionName
223- + "'" );
224- }
212+ String claimError = claimExpositionNames (expositionNames , prometheusName );
213+ if (claimError != null ) {
214+ throw new IllegalArgumentException (claimError );
225215 }
226216
227- registered .compute (
228- prometheusName ,
229- (n , existingInfo ) -> {
230- if (existingInfo == null ) {
231- return RegistrationInfo .of (type , names , helpForValidation , unitForValidation );
232- } else {
233- if (existingInfo .getType () != type ) {
234- throw new IllegalArgumentException (
235- prometheusName
236- + ": Conflicting metric types. Existing: "
237- + existingInfo .getType ()
238- + ", new: "
239- + type );
240- }
241- // Check label set first; only mutate help/unit after validation passes.
242- if (!existingInfo .addLabelSet (names )) {
243- throw new IllegalArgumentException (
244- prometheusName + ": duplicate metric name with identical label schema " + names );
245- }
246- // Roll back label schema if metadata validation fails
247- try {
248- existingInfo .validateMetadata (helpForValidation , unitForValidation );
249- } catch (IllegalArgumentException e ) {
250- existingInfo .removeLabelSet (names );
251- throw e ;
217+ boolean success = false ;
218+ try {
219+ registered .compute (
220+ prometheusName ,
221+ (n , existingInfo ) -> {
222+ if (existingInfo == null ) {
223+ return RegistrationInfo .of (type , names , helpForValidation , unitForValidation );
224+ } else {
225+ if (existingInfo .getType () != type ) {
226+ throw new IllegalArgumentException (
227+ prometheusName
228+ + ": Conflicting metric types. Existing: "
229+ + existingInfo .getType ()
230+ + ", new: "
231+ + type );
232+ }
233+ // Check label set first; only mutate help/unit after validation passes.
234+ if (!existingInfo .addLabelSet (names )) {
235+ throw new IllegalArgumentException (
236+ prometheusName
237+ + ": duplicate metric name with identical label schema "
238+ + names );
239+ }
240+ // Roll back label schema if metadata validation fails
241+ try {
242+ existingInfo .validateMetadata (helpForValidation , unitForValidation );
243+ } catch (IllegalArgumentException e ) {
244+ existingInfo .removeLabelSet (names );
245+ throw e ;
246+ }
247+ return existingInfo ;
252248 }
253- return existingInfo ;
254- }
255- });
249+ });
250+ success = true ;
251+ } finally {
252+ if (!success ) {
253+ releaseExpositionNames (expositionNames , prometheusName );
254+ }
255+ }
256+ }
257+
258+ /**
259+ * Atomically claims exposition names for the given metric. Returns null on success, or an error
260+ * message on conflict. Rolls back any partially claimed names on failure.
261+ */
262+ @ Nullable
263+ private String claimExpositionNames (Set <String > expositionNames , String prometheusName ) {
264+ for (String expositionName : expositionNames ) {
265+ String existingOwner = expositionNameOwners .putIfAbsent (expositionName , prometheusName );
266+ if (existingOwner != null && !existingOwner .equals (prometheusName )) {
267+ releaseExpositionNames (expositionNames , prometheusName );
268+ return "'"
269+ + prometheusName
270+ + "' and '"
271+ + existingOwner
272+ + "' have conflicting exposition name: '"
273+ + expositionName
274+ + "'" ;
275+ }
276+ }
277+ return null ;
278+ }
256279
257- // Registration succeeded — claim exposition names.
280+ private void releaseExpositionNames ( Set < String > expositionNames , String owner ) {
258281 for (String expositionName : expositionNames ) {
259- expositionNameOwners .put (expositionName , prometheusName );
282+ expositionNameOwners .remove (expositionName , owner );
260283 }
261284 }
262285
0 commit comments