11package org .hypertrace .label .config .service ;
22
3+ import static java .util .function .Function .identity ;
4+
35import com .google .common .util .concurrent .Striped ;
46import com .google .protobuf .Duration ;
57import com .google .protobuf .util .JsonFormat ;
1113import io .grpc .stub .StreamObserver ;
1214import java .util .ArrayList ;
1315import java .util .Collections ;
16+ import java .util .HashMap ;
1417import java .util .List ;
1518import java .util .Map ;
1619import java .util .UUID ;
1720import java .util .concurrent .TimeUnit ;
1821import java .util .concurrent .locks .Lock ;
19- import java .util .function .Function ;
2022import java .util .stream .Collectors ;
2123import lombok .SneakyThrows ;
2224import lombok .extern .slf4j .Slf4j ;
3335import org .hypertrace .label .config .service .v1 .GetLabelResponse ;
3436import org .hypertrace .label .config .service .v1 .GetLabelsRequest ;
3537import org .hypertrace .label .config .service .v1 .GetLabelsResponse ;
38+ import org .hypertrace .label .config .service .v1 .GetOrCreateLabelsRequest ;
39+ import org .hypertrace .label .config .service .v1 .GetOrCreateLabelsResponse ;
3640import org .hypertrace .label .config .service .v1 .Label ;
3741import org .hypertrace .label .config .service .v1 .LabelData ;
3842import org .hypertrace .label .config .service .v1 .LabelsConfigServiceGrpc ;
@@ -71,13 +75,11 @@ public LabelsConfigServiceImpl(
7175 if (systemLabelsObjectList != null ) {
7276 systemLabels = buildSystemLabelList (systemLabelsObjectList );
7377 systemLabelsIdLabelMap =
74- systemLabels .stream ()
75- .collect (Collectors .toUnmodifiableMap (Label ::getId , Function .identity ()));
78+ systemLabels .stream ().collect (Collectors .toUnmodifiableMap (Label ::getId , identity ()));
7679 systemLabelsKeyLabelMap =
7780 systemLabels .stream ()
7881 .collect (
79- Collectors .toUnmodifiableMap (
80- (label ) -> label .getData ().getKey (), Function .identity ()));
82+ Collectors .toUnmodifiableMap ((label ) -> label .getData ().getKey (), identity ()));
8183 } else {
8284 systemLabels = Collections .emptyList ();
8385 systemLabelsIdLabelMap = Collections .emptyMap ();
@@ -108,8 +110,7 @@ public void createLabel(
108110 if (labelsLock .tryLock (WAIT_TIME .getSeconds (), TimeUnit .SECONDS )) {
109111 try {
110112 LabelData labelData = request .getData ();
111- if (systemLabelsKeyLabelMap .containsKey (labelData .getKey ())
112- || isDuplicateKey (labelData .getKey ())) {
113+ if (isDuplicateKey (requestContext , labelData .getKey ())) {
113114 // Creating a label with a name that clashes with one of system labels name
114115 responseObserver .onError (new StatusRuntimeException (Status .ALREADY_EXISTS ));
115116 return ;
@@ -136,6 +137,66 @@ public void createLabel(
136137 }
137138 }
138139
140+ @ Override
141+ public void getOrCreateLabels (
142+ GetOrCreateLabelsRequest request ,
143+ StreamObserver <GetOrCreateLabelsResponse > responseObserver ) {
144+ try {
145+ RequestContext requestContext = RequestContext .CURRENT .get ();
146+ Lock labelsLock = this .stripedLabelsLock .get (requestContext .getTenantId ());
147+ if (labelsLock .tryLock (WAIT_TIME .getSeconds (), TimeUnit .SECONDS )) {
148+ try {
149+ final Map <String , Label > existingLabelsMap = getLabelsMap (requestContext );
150+ List <Label > newLabels =
151+ request .getRequestsList ().stream ()
152+ .filter (
153+ labelRequest ->
154+ !existingLabelsMap .containsKey (labelRequest .getData ().getKey ()))
155+ .map (this ::buildLabelFromRequest )
156+ .collect (Collectors .toList ());
157+ Map <String , Label > createdLabelsMap ;
158+ if (!newLabels .isEmpty ()) {
159+ createdLabelsMap =
160+ labelStore .upsertObjects (requestContext , newLabels ).stream ()
161+ .map (org .hypertrace .config .objectstore .ConfigObject ::getData )
162+ .collect (
163+ Collectors .toUnmodifiableMap (
164+ label -> label .getData ().getKey (), identity ()));
165+ } else {
166+ createdLabelsMap = Collections .emptyMap ();
167+ }
168+ final Map <String , Label > allLabelsMap = new HashMap <>();
169+ allLabelsMap .putAll (existingLabelsMap );
170+ allLabelsMap .putAll (createdLabelsMap );
171+ List <Label > allLabels =
172+ request .getRequestsList ().stream ()
173+ .map (GetOrCreateLabelsRequest .LabelRequest ::getData )
174+ .map (data -> allLabelsMap .get (data .getKey ()))
175+ .collect (Collectors .toList ());
176+ responseObserver .onNext (
177+ GetOrCreateLabelsResponse .newBuilder ().addAllLabels (allLabels ).build ());
178+ responseObserver .onCompleted ();
179+ } finally {
180+ labelsLock .unlock ();
181+ }
182+ } else {
183+ responseObserver .onError (new StatusRuntimeException (Status .ABORTED ));
184+ }
185+ } catch (Exception e ) {
186+ responseObserver .onError (e );
187+ }
188+ }
189+
190+ private Label buildLabelFromRequest (GetOrCreateLabelsRequest .LabelRequest request ) {
191+ LabelData labelData = request .getData ();
192+ Label .Builder labelBuilder =
193+ Label .newBuilder ().setId (UUID .randomUUID ().toString ()).setData (labelData );
194+ if (request .hasCreatedByApplicationRuleId ()) {
195+ labelBuilder .setCreatedByApplicationRuleId (request .getCreatedByApplicationRuleId ());
196+ }
197+ return labelBuilder .build ();
198+ }
199+
139200 @ Override
140201 public void getLabel (GetLabelRequest request , StreamObserver <GetLabelResponse > responseObserver ) {
141202 RequestContext requestContext = RequestContext .CURRENT .get ();
@@ -180,13 +241,12 @@ public void updateLabel(
180241 Lock labelsLock = this .stripedLabelsLock .get (requestContext .getTenantId ());
181242 if (labelsLock .tryLock (WAIT_TIME .getSeconds (), TimeUnit .SECONDS )) {
182243 try {
183- if (systemLabelsIdLabelMap .containsKey (request .getId ())
184- || systemLabelsKeyLabelMap .containsKey (updateLabelData .getKey ())) {
244+ if (systemLabelsIdLabelMap .containsKey (request .getId ())) {
185245 // Updating a system label will error
186246 responseObserver .onError (new StatusRuntimeException (Status .INVALID_ARGUMENT ));
187247 return ;
188248 }
189- if (isDuplicateKey (updateLabelData .getKey ())) {
249+ if (isDuplicateKey (requestContext , updateLabelData .getKey ())) {
190250 responseObserver .onError (new StatusRuntimeException (Status .ALREADY_EXISTS ));
191251 return ;
192252 }
@@ -241,15 +301,16 @@ public void deleteLabel(
241301 }
242302 }
243303
244- private boolean isDuplicateKey (String key ) {
245- RequestContext requestContext = RequestContext .CURRENT .get ();
246- List <Label > labelList =
247- labelStore .getAllObjects (requestContext ).stream ()
248- .map (ContextualConfigObject ::getData )
249- .collect (Collectors .toUnmodifiableList ());
250- return labelList .stream ()
251- .map (Label ::getData )
252- .map (LabelData ::getKey )
253- .anyMatch (labelKey -> labelKey .equals (key ));
304+ private boolean isDuplicateKey (RequestContext requestContext , String key ) {
305+ return getLabelsMap (requestContext ).containsKey (key );
306+ }
307+
308+ private Map <String , Label > getLabelsMap (RequestContext requestContext ) {
309+ Map <String , Label > existingLabelsMap = new HashMap <>();
310+ existingLabelsMap .putAll (systemLabelsKeyLabelMap );
311+ labelStore .getAllObjects (requestContext ).stream ()
312+ .map (ContextualConfigObject ::getData )
313+ .forEach (label -> existingLabelsMap .put (label .getData ().getKey (), label ));
314+ return Collections .unmodifiableMap (existingLabelsMap );
254315 }
255316}
0 commit comments