@@ -88,8 +88,12 @@ abstract class BaseExperimentAsync extends BaseExperiment {
8888 this .baseContext = ExperimentContext .empty ();
8989 }
9090
91- void updateContext (ExperimentContext context ) {
92- this .baseContext .mergeFrom (context );
91+ ExperimentContext mergeWithBaseContextIfEmpty (ExperimentContext context ) {
92+ if (context .isEmpty ()) {
93+ return new ExperimentContext (this .baseContext );
94+ } else {
95+ return context ;
96+ }
9397 }
9498
9599 /**
@@ -104,13 +108,13 @@ void updateContext(ExperimentContext context) {
104108 @ SuppressWarnings ("OptionalUsedAsFieldOrParameterType" )
105109 void logMetric (@ NonNull String metricName , @ NonNull Object metricValue ,
106110 @ NonNull ExperimentContext context , @ NonNull Optional <Action > onComplete ) {
107- this . updateContext (context );
111+ ExperimentContext ctx = mergeWithBaseContextIfEmpty (context );
108112
109113 if (getLogger ().isDebugEnabled ()) {
110- getLogger ().debug ("logMetricAsync {} = {}, context: {}" , metricName , metricValue , context );
114+ getLogger ().debug ("logMetricAsync {} = {}, context: {}" , metricName , metricValue , ctx );
111115 }
112116
113- MetricRest metricRequest = createLogMetricRequest (metricName , metricValue , this . baseContext );
117+ MetricRest metricRequest = createLogMetricRequest (metricName , metricValue , ctx );
114118 this .sendAsynchronously (getRestApiClient ()::logMetric , metricRequest , onComplete );
115119 }
116120
@@ -126,13 +130,13 @@ void logMetric(@NonNull String metricName, @NonNull Object metricValue,
126130 @ SuppressWarnings ("OptionalUsedAsFieldOrParameterType" )
127131 void logParameter (@ NonNull String parameterName , @ NonNull Object paramValue ,
128132 @ NonNull ExperimentContext context , @ NonNull Optional <Action > onComplete ) {
129- this . updateContext (context );
133+ ExperimentContext ctx = mergeWithBaseContextIfEmpty (context );
130134
131135 if (getLogger ().isDebugEnabled ()) {
132- getLogger ().debug ("logParameterAsync {} = {}, context: {}" , parameterName , paramValue , context );
136+ getLogger ().debug ("logParameterAsync {} = {}, context: {}" , parameterName , paramValue , ctx );
133137 }
134138
135- ParameterRest paramRequest = createLogParamRequest (parameterName , paramValue , this . baseContext );
139+ ParameterRest paramRequest = createLogParamRequest (parameterName , paramValue , ctx );
136140 this .sendAsynchronously (getRestApiClient ()::logParameter , paramRequest , onComplete );
137141 }
138142
@@ -316,10 +320,7 @@ void logAssetFolder(@NonNull File folder, boolean logFilePath, boolean recursive
316320 getLogger ().warn (getString (LOG_ASSET_FOLDER_EMPTY , folder ));
317321 return ;
318322 }
319- this .updateContext (context );
320- // make deep copy of the current experiment context to avoid side effects
321- // if base experiment context become updated while operation is still in progress
322- ExperimentContext assetContext = new ExperimentContext (this .baseContext );
323+ ExperimentContext assetContext = mergeWithBaseContextIfEmpty (context );
323324
324325 AtomicInteger successfullyLoggedCount = new AtomicInteger ();
325326 try {
@@ -381,10 +382,10 @@ void logAssetFolder(@NonNull File folder, boolean logFilePath, boolean recursive
381382 void logRemoteAsset (@ NonNull URI uri , Optional <String > logicalPath , boolean overwrite ,
382383 @ NonNull Optional <Map <String , Object >> metadata , @ NonNull ExperimentContext context ,
383384 @ NonNull Optional <Action > onComplete ) {
384- this . updateContext (context );
385+ ExperimentContext ctx = mergeWithBaseContextIfEmpty (context );
385386
386387 RemoteAssetImpl asset = AssetUtils .createRemoteAsset (uri , logicalPath , overwrite , metadata , empty ());
387- this .logAssetAsync (getRestApiClient ()::logRemoteAsset , asset , onComplete );
388+ this .logAssetAsync (getRestApiClient ()::logRemoteAsset , asset , ctx , onComplete );
388389
389390 if (Objects .equals (asset .getLogicalPath (), AssetUtils .REMOTE_FILE_NAME_DEFAULT )) {
390391 getLogger ().warn (
@@ -509,6 +510,7 @@ void updateArtifactVersionState(@NonNull LoggedArtifact loggedArtifact,
509510 * @param assetType the type of the asset.
510511 * @param groupingName optional name of group this asset should belong.
511512 * @param metadata the optional metadata to associate.
513+ * @param context the experiment context to be associated with given assets.
512514 * @param onComplete The optional action to be invoked when this operation asynchronously completes.
513515 * Can be {@code null} if not interested in completion signal.
514516 */
@@ -519,11 +521,12 @@ void logAssetDataAsync(byte[] data, @NonNull String fileName, boolean overwrite,
519521 @ NonNull Optional <Map <String , Object >> metadata ,
520522 @ NonNull ExperimentContext context ,
521523 @ NonNull Optional <Action > onComplete ) {
522- this .updateContext (context );
523524
524525 AssetImpl asset = createAssetFromData (data , fileName , overwrite , metadata , assetType );
525526 groupingName .ifPresent (asset ::setGroupingName );
526- this .logAssetAsync (asset , onComplete );
527+ ExperimentContext ctx = mergeWithBaseContextIfEmpty (context );
528+
529+ this .logAssetAsync (asset , ctx , onComplete );
527530 }
528531
529532 /**
@@ -535,6 +538,7 @@ void logAssetDataAsync(byte[] data, @NonNull String fileName, boolean overwrite,
535538 * @param assetType the type of the asset.
536539 * @param groupingName optional name of group this asset should belong.
537540 * @param metadata the optional metadata to associate.
541+ * @param context the experiment context to be associated with given assets.
538542 * @param onComplete The optional action to be invoked when this operation asynchronously completes.
539543 * Can be {@code null} if not interested in completion signal.
540544 */
@@ -545,11 +549,12 @@ void logAssetFileAsync(@NonNull File file, @NonNull String fileName, boolean ove
545549 @ NonNull Optional <Map <String , Object >> metadata ,
546550 @ NonNull ExperimentContext context ,
547551 @ NonNull Optional <Action > onComplete ) {
548- this .updateContext (context );
549552
550553 AssetImpl asset = createAssetFromFile (file , Optional .of (fileName ), overwrite , metadata , assetType );
551554 groupingName .ifPresent (asset ::setGroupingName );
552- this .logAssetAsync (asset , onComplete );
555+ ExperimentContext ctx = mergeWithBaseContextIfEmpty (context );
556+
557+ this .logAssetAsync (asset , ctx , onComplete );
553558 }
554559
555560 @ SuppressWarnings ("OptionalUsedAsFieldOrParameterType" )
@@ -565,27 +570,31 @@ void logAssetFileAsync(@NonNull File file, @NonNull String fileName, boolean ove
565570 * Asynchronously logs provided asset and signals upload completion if {@code onComplete} action provided.
566571 *
567572 * @param asset the {@link Asset} to be uploaded.
573+ * @param context the experiment context to be associated with given assets.
568574 * @param onComplete the optional {@link Action} to be called upon operation completed,
569575 * either successful or failure.
570576 */
571577 @ SuppressWarnings ("OptionalUsedAsFieldOrParameterType" )
572- private void logAssetAsync (@ NonNull final Asset asset , @ NonNull Optional <Action > onComplete ) {
573- this .logAssetAsync (getRestApiClient ()::logAsset , asset , onComplete );
578+ private void logAssetAsync (@ NonNull final Asset asset , @ NonNull ExperimentContext context ,
579+ @ NonNull Optional <Action > onComplete ) {
580+ this .logAssetAsync (getRestApiClient ()::logAsset , asset , context , onComplete );
574581 }
575582
576583 /**
577584 * Attempts to log provided {@link AssetImpl} or its subclass asynchronously using specified log function.
578585 *
586+ * @param <T> the {@link AssetImpl} or its subclass.
579587 * @param func the function to be invoked to send asset to the backend.
580588 * @param asset the {@link AssetImpl} or subclass to be sent.
589+ * @param context the experiment context to be associated with given assets.
581590 * @param onComplete The optional action to be invoked when this operation
582591 * asynchronously completes. Can be empty if not interested in completion signal.
583- * @param <T> the {@link AssetImpl} or its subclass.
584592 */
585593 @ SuppressWarnings ("OptionalUsedAsFieldOrParameterType" )
586594 private <T extends Asset > void logAssetAsync (@ NonNull final BiFunction <T , String , Single <RestApiResponse >> func ,
587- @ NonNull final T asset , @ NonNull Optional <Action > onComplete ) {
588- ((AssetImpl ) asset ).setContext (this .baseContext );
595+ @ NonNull final T asset , @ NonNull ExperimentContext context ,
596+ @ NonNull Optional <Action > onComplete ) {
597+ ((AssetImpl ) asset ).setContext (context );
589598 Single <RestApiResponse > single = this .sendAssetAsync (func , asset );
590599
591600 if (onComplete .isPresent ()) {
0 commit comments