Skip to content

Commit b64feb7

Browse files
dhanendra06kameshsrashok-ksharma
authored
MOSIP-44198 (#303)
* MOSIP-44206 Update khazana dependency version (#298) Updated version of 'khazana' dependency to 1.3.1-SNAPSHOT. Signed-off-by: kameshsr <47484458+kameshsr@users.noreply.github.com> Signed-off-by: Dhanendra Sahu <dhanendra.tech@gmail.com> * MOSIP-44198 Added loggers (#299) Signed-off-by: kameshsr <kameshsr1338@gmail.com> Signed-off-by: Dhanendra Sahu <dhanendra.tech@gmail.com> * Fix the datasahre slowness issue under the high load Signed-off-by: Dhanendra Sahu <dhanendra.tech@gmail.com> * Changed to DateUtils2 Signed-off-by: Dhanendra Sahu <dhanendra.tech@gmail.com> * Changed the runner policy Signed-off-by: Dhanendra Sahu <dhanendra.tech@gmail.com> * Fix the network overhead issue Signed-off-by: Dhanendra Sahu <dhanendra.tech@gmail.com> * Revert "Fix the network overhead issue" This reverts commit 9645fa0. Signed-off-by: Dhanendra Sahu <dhanendra.tech@gmail.com> * Changed the khazana version Signed-off-by: Dhanendra Sahu <dhanendra.tech@gmail.com> * removed unused changes Signed-off-by: Dhanendra Sahu <dhanendra.tech@gmail.com> * [MOSIP-44198] Code fixes post review. Signed-off-by: Ashok Kumar Sharma <ashok@mosip.io> --------- Signed-off-by: kameshsr <47484458+kameshsr@users.noreply.github.com> Signed-off-by: Dhanendra Sahu <dhanendra.tech@gmail.com> Signed-off-by: kameshsr <kameshsr1338@gmail.com> Signed-off-by: Ashok Kumar Sharma <ashok@mosip.io> Co-authored-by: kameshsr <47484458+kameshsr@users.noreply.github.com> Co-authored-by: Ashok Kumar Sharma <ashok@mosip.io>
1 parent 20d2f61 commit b64feb7

8 files changed

Lines changed: 307 additions & 231 deletions

File tree

data-share/data-share-service/pom.xml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
<parent>
66
<groupId>io.mosip.datashare</groupId>
77
<artifactId>data-share</artifactId>
8-
<version>1.3.0</version>
8+
<version>1.3.1-SNAPSHOT</version>
99
</parent>
1010

1111
<artifactId>data-share-service</artifactId>
12-
<version>1.3.0</version>
12+
<version>1.3.1-SNAPSHOT</version>
1313
<name>data-share-service</name>
1414

1515
<properties>
@@ -22,7 +22,7 @@
2222
<dependency>
2323
<groupId>io.mosip.kernel</groupId>
2424
<artifactId>kernel-bom</artifactId>
25-
<version>1.3.0</version>
25+
<version>1.3.1-SNAPSHOT</version>
2626
<type>pom</type>
2727
<scope>import</scope>
2828
</dependency>
@@ -94,7 +94,7 @@
9494
<dependency>
9595
<groupId>io.mosip.commons</groupId>
9696
<artifactId>khazana</artifactId>
97-
<version>1.3.0</version>
97+
<version>1.3.1-SNAPSHOT</version>
9898
</dependency>
9999
<dependency>
100100
<groupId>org.powermock</groupId>
@@ -303,3 +303,4 @@
303303
</profiles>
304304

305305
</project>
306+

data-share/data-share-service/src/main/java/io/mosip/datashare/config/DataShareBeanConfig.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,17 @@
44
import com.fasterxml.jackson.databind.ObjectMapper;
55
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
66
import com.fasterxml.jackson.module.afterburner.AfterburnerModule;
7+
import java.util.concurrent.ThreadPoolExecutor;
8+
9+
import org.springframework.beans.factory.annotation.Value;
710
import org.springframework.cache.annotation.EnableCaching;
811
import org.springframework.context.annotation.Bean;
912
import org.springframework.context.annotation.Configuration;
1013
import org.springframework.context.annotation.Primary;
1114
import org.springframework.context.annotation.PropertySource;
15+
import org.springframework.core.task.TaskExecutor;
1216
import org.springframework.scheduling.annotation.EnableScheduling;
17+
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
1318

1419
import io.mosip.commons.khazana.impl.S3Adapter;
1520
import io.mosip.commons.khazana.spi.ObjectStoreAdapter;
@@ -22,6 +27,33 @@
2227
@PropertySource("classpath:bootstrap.properties")
2328
public class DataShareBeanConfig {
2429

30+
@Value("${mosip.data.share.async.core-pool-size:25}")
31+
private int asyncCorePoolSize;
32+
33+
@Value("${mosip.data.share.async.max-pool-size:50}")
34+
private int asyncMaxPoolSize;
35+
36+
@Value("${mosip.data.share.async.queue-capacity:80}")
37+
private int asyncQueueCapacity;
38+
39+
/**
40+
* Thread pool for parallelising independent async operations within a request
41+
* (e.g. encryption + JWT signing). Sizes are tunable via properties.
42+
*/
43+
@Bean(name = "dataShareTaskExecutor")
44+
public TaskExecutor dataShareTaskExecutor() {
45+
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
46+
executor.setCorePoolSize(asyncCorePoolSize);
47+
executor.setMaxPoolSize(asyncMaxPoolSize);
48+
executor.setQueueCapacity(asyncQueueCapacity);
49+
executor.setThreadNamePrefix("datashare-async-");
50+
// CallerRunsPolicy: when pool is saturated, run on the calling (Tomcat) thread
51+
// instead of rejecting. Gracefully degrades to sequential under extreme load.
52+
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
53+
executor.initialize();
54+
return executor;
55+
}
56+
2557
@Bean
2658
public ObjectStoreAdapter objectStoreAdapter() {
2759
return new S3Adapter();

data-share/data-share-service/src/main/java/io/mosip/datashare/service/impl/DataShareServiceImpl.java

Lines changed: 75 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,19 @@
88
import java.time.format.DateTimeFormatter;
99
import java.util.HashMap;
1010
import java.util.Map;
11+
import java.util.concurrent.CompletableFuture;
12+
import java.util.concurrent.CompletionException;
13+
import java.util.concurrent.ExecutionException;
1114

1215
import io.mosip.commons.khazana.exception.ObjectStoreAdapterException;
1316
import jakarta.annotation.PostConstruct;
1417
import org.apache.commons.io.IOUtils;
1518
import org.springframework.beans.factory.annotation.Autowired;
19+
import org.springframework.beans.factory.annotation.Qualifier;
1620
import org.springframework.beans.factory.annotation.Value;
1721
import org.springframework.cloud.context.config.annotation.RefreshScope;
1822
import org.springframework.core.env.Environment;
23+
import org.springframework.core.task.TaskExecutor;
1924
import org.springframework.stereotype.Component;
2025
import org.springframework.web.multipart.MultipartFile;
2126

@@ -74,13 +79,26 @@ public class DataShareServiceImpl implements DataShareService {
7479
@Autowired
7580
private CacheUtil cacheUtil;
7681

82+
/** Shared executor for parallelising independent per-request operations. */
83+
@Autowired
84+
@Qualifier("dataShareTaskExecutor")
85+
private TaskExecutor taskExecutor;
7786

7887
/** The Constant KEY_LENGTH. */
7988
private static final String KEY_LENGTH = "mosip.data.share.key.length";
8089

8190
/** The Constant DEFAULT_KEY_LENGTH. */
8291
private static final int DEFAULT_KEY_LENGTH = 8;
8392

93+
/**
94+
* Singleton SecureRandom — thread-safe; avoids per-request OS seeding overhead.
95+
*/
96+
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
97+
98+
/** Key length for random share keys, cached from config at startup. */
99+
@Value("${" + KEY_LENGTH + ":" + DEFAULT_KEY_LENGTH + "}")
100+
private int keyLength;
101+
84102
/** The Constant IO_EXCEPTION. */
85103
private static final String IO_EXCEPTION = "Exception while reading file";
86104

@@ -162,30 +180,43 @@ public DataShare createDataShare(String policyId, String subscriberId, Multipart
162180
} else {
163181
dataSharePolicy = policyUtil.getStaticDataSharePolicy(policyId, subscriberId, usageCountForStandaloneMode);
164182
}
165-
byte[] encryptedData = null;
166-
if (PARTNERBASED.equalsIgnoreCase(dataSharePolicy.getEncryptionType())) {
167-
LOGGER.info(LoggerFileConstant.SESSIONID.toString(), LoggerFileConstant.POLICYID.toString(),
168-
policyId, subscriberId + "encryptionNeeded" + dataSharePolicy.getEncryptionType());
169-
encryptedData = encryptionUtil.encryptData(fileData, subscriberId);
170-
171-
} else if (NONE.equalsIgnoreCase(dataSharePolicy.getEncryptionType())) {
172-
173-
encryptedData = fileData;
174-
LOGGER.info(LoggerFileConstant.SESSIONID.toString(), LoggerFileConstant.POLICYID.toString(),
175-
policyId, subscriberId + "Without encryption" + dataSharePolicy.getEncryptionType());
176-
177-
}
178-
179183
String createShareTime = DateUtils2
180184
.getUTCCurrentDateTimeString(env.getProperty(DATETIME_PATTERN));
181185
String expiryTime = DateUtils2
182186
.toISOString(DateUtils2.addMinutes(DateUtils2.parseUTCToDate(createShareTime),
183187
Integer.parseInt(dataSharePolicy.getValidForInMinutes())));
184188

185-
String jwtSignature = "";
186-
if(!isSignatureDisabled) {
187-
jwtSignature = digitalSignatureUtil.jwtSign(fileData, file.getName(), subscriberId,
188-
createShareTime, expiryTime);
189+
// Launch encryption and JWT signing in parallel — they are independent of each other
190+
final String encType = dataSharePolicy.getEncryptionType();
191+
192+
CompletableFuture<byte[]> encryptFuture;
193+
if (PARTNERBASED.equalsIgnoreCase(encType)) {
194+
LOGGER.info(LoggerFileConstant.SESSIONID.toString(), LoggerFileConstant.POLICYID.toString(),
195+
policyId, subscriberId + "encryptionNeeded" + encType);
196+
encryptFuture = CompletableFuture.supplyAsync(
197+
() -> encryptionUtil.encryptData(fileData, subscriberId), taskExecutor);
198+
} else if (NONE.equalsIgnoreCase(encType)) {
199+
LOGGER.info(LoggerFileConstant.SESSIONID.toString(), LoggerFileConstant.POLICYID.toString(),
200+
policyId, subscriberId + "Without encryption" + encType);
201+
encryptFuture = CompletableFuture.completedFuture(fileData);
202+
} else {
203+
encryptFuture = CompletableFuture.completedFuture(null);
204+
}
205+
206+
CompletableFuture<String> signFuture = isSignatureDisabled
207+
? CompletableFuture.completedFuture("")
208+
: CompletableFuture.supplyAsync(
209+
() -> digitalSignatureUtil.jwtSign(fileData, file.getName(), subscriberId,
210+
createShareTime, expiryTime), taskExecutor);
211+
212+
byte[] encryptedData;
213+
String jwtSignature;
214+
try {
215+
CompletableFuture.allOf(encryptFuture, signFuture).join();
216+
encryptedData = encryptFuture.join();
217+
jwtSignature = signFuture.join();
218+
} catch (CompletionException ce) {
219+
throw completionCauseUnwrapped(ce);
189220
}
190221
Map<String, Object> aclMap = prepareMetaData(subscriberId, policyId, dataSharePolicy,
191222
jwtSignature, policyPublishDate);
@@ -231,12 +262,7 @@ private String constructURL(String randomShareKey, DataShareDto dataSharePolicy,
231262
String protocol = (dataSharePolicy.getProtocol() != null) ? dataSharePolicy.getProtocol() :HTTP_PROTOCOL ;
232263
String url = null;
233264
if (isShortUrl) {
234-
int length = DEFAULT_KEY_LENGTH;
235-
if (env.getProperty(KEY_LENGTH) != null) {
236-
length = Integer.parseInt(env.getProperty(KEY_LENGTH));
237-
}
238-
239-
String shortRandomShareKey = generateShortRandomShareKey(length);
265+
String shortRandomShareKey = generateShortRandomShareKey(keyLength);
240266
cacheUtil.getShortUrlData(shortRandomShareKey, policyId, subscriberId, randomShareKey);
241267
url = dataSharePolicy.getShareDomainUrlRead() != null ?
242268
dataSharePolicy.getShareDomainUrlRead() +
@@ -388,14 +414,9 @@ private Map<String, Object> prepareMetaData(String subscriberId, String policyId
388414
*/
389415
private String storefile(Map<String, Object> metaDataMap, InputStream filedata, String policyId,
390416
String subscriberId) {
391-
int length = DEFAULT_KEY_LENGTH;
392-
if (env.getProperty(KEY_LENGTH) != null) {
393-
length = Integer.parseInt(env.getProperty(KEY_LENGTH));
394-
}
395-
396417
String randomShareKey = subscriberId + policyId
397418
+ DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now())
398-
+ generateShortRandomShareKey(length);
419+
+ generateShortRandomShareKey(keyLength);
399420
boolean isDataStored = objectStoreAdapter.putObject(subscriberId, policyId, null, null, randomShareKey,
400421
filedata);
401422
objectStoreAdapter.addObjectMetaData(subscriberId, policyId, null, null, randomShareKey, metaDataMap);
@@ -433,10 +454,31 @@ public DataShareGetResponse getDataFile(String shortUrlKey) {
433454

434455
}
435456

436-
private String generateShortRandomShareKey(int byteLength) {
437-
SecureRandom secureRandom = new SecureRandom();
457+
/**
458+
* Unwraps {@code CompletableFuture.join()} failures so callers see the same throwable as
459+
* synchronous {@code encryptData} / {@code jwtSign} (including nested
460+
* {@link CompletionException} / {@link ExecutionException} from {@code allOf} / executors).
461+
*/
462+
private static RuntimeException completionCauseUnwrapped(CompletionException ce) {
463+
Throwable t = ce;
464+
while ((t instanceof CompletionException || t instanceof ExecutionException) && t.getCause() != null) {
465+
t = t.getCause();
466+
}
467+
if (t instanceof Error) {
468+
throw (Error) t;
469+
}
470+
if (t instanceof RuntimeException) {
471+
return (RuntimeException) t;
472+
}
473+
if (t != null) {
474+
return new CompletionException(t);
475+
}
476+
return ce;
477+
}
478+
479+
private static String generateShortRandomShareKey(int byteLength) {
438480
byte[] token = new byte[byteLength];
439-
secureRandom.nextBytes(token);
481+
SECURE_RANDOM.nextBytes(token);
440482
return CryptoUtil.encodeToURLSafeBase64(token);
441483
}
442484

data-share/data-share-service/src/main/java/io/mosip/datashare/util/DigitalSignatureUtil.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package io.mosip.datashare.util;
22

33
import java.io.IOException;
4-
import java.nio.charset.StandardCharsets;
54
import java.time.LocalDateTime;
6-
import java.time.ZoneOffset;
75
import java.time.format.DateTimeFormatter;
86

97
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -33,7 +31,7 @@
3331
import io.mosip.kernel.core.http.RequestWrapper;
3432
import io.mosip.kernel.core.logger.spi.Logger;
3533
import io.mosip.kernel.core.util.CryptoUtil;
36-
import io.mosip.kernel.core.util.DateUtils;
34+
import io.mosip.kernel.core.util.DateUtils2;
3735
import io.mosip.kernel.core.util.HMACUtils2;
3836

3937

@@ -114,7 +112,7 @@ public String jwtSign(byte[] file, String filname, String partnerId, String crea
114112
request.setMetadata(null);
115113

116114
// Step 2: Generate UTC timestamp in configured pattern
117-
LocalDateTime nowUtc = LocalDateTime.parse(DateUtils.getUTCCurrentDateTimeString(dateTimePattern), formatter);
115+
LocalDateTime nowUtc = LocalDateTime.parse(DateUtils2.getUTCCurrentDateTimeString(dateTimePattern), formatter);
118116
request.setRequesttime(nowUtc);
119117

120118
String responseString = restUtil.postApi(ApiName.KEYMANAGER_JWTSIGN, null, "", "",

data-share/data-share-service/src/main/java/io/mosip/datashare/util/EncryptionUtil.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import com.fasterxml.jackson.databind.ObjectMapper;
1111
import com.fasterxml.jackson.databind.ObjectReader;
1212

13-
import io.mosip.kernel.core.util.DateUtils;
13+
import io.mosip.kernel.core.util.DateUtils2;
1414
import jakarta.annotation.PostConstruct;
1515

1616
import org.springframework.beans.factory.annotation.Autowired;
@@ -108,7 +108,7 @@ public byte[] encryptData(byte[] filedata, String partnerId) {
108108
cryptomanagerRequestDto.setData(dataToBeEncrypted);
109109
cryptomanagerRequestDto.setReferenceId(partnerId);
110110
cryptomanagerRequestDto.setPrependThumbprint(prependThumbprint);
111-
LocalDateTime localdatetime = LocalDateTime.parse(DateUtils.getUTCCurrentDateTimeString(dateTimePattern), formatter);
111+
LocalDateTime localdatetime = LocalDateTime.parse(DateUtils2.getUTCCurrentDateTimeString(dateTimePattern), formatter);
112112
request.setRequesttime(localdatetime);
113113

114114
request.setRequest(cryptomanagerRequestDto);

data-share/data-share-service/src/test/java/io/mosip/datashare/test/service/impl/DataShareServiceImplTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.powermock.modules.junit4.PowerMockRunner;
2727
import org.powermock.modules.junit4.PowerMockRunnerDelegate;
2828
import org.springframework.core.env.Environment;
29+
import org.springframework.core.task.SyncTaskExecutor;
2930
import org.springframework.mock.web.MockMultipartFile;
3031
import org.springframework.test.context.junit4.SpringRunner;
3132
import org.springframework.test.util.ReflectionTestUtils;
@@ -97,6 +98,8 @@ public void setUp() throws Exception {
9798
ReflectionTestUtils.setField(dataShareServiceImpl, "servletPath", "/");
9899
ReflectionTestUtils.setField(dataShareServiceImpl, "isShortUrl", false);
99100
ReflectionTestUtils.setField(dataShareServiceImpl, "httpProtocol", "https");
101+
ReflectionTestUtils.setField(dataShareServiceImpl, "taskExecutor", new SyncTaskExecutor());
102+
ReflectionTestUtils.setField(dataShareServiceImpl, "keyLength", 8);
100103
Mockito.when(env.getProperty("mosip.data.share.datetime.pattern"))
101104
.thenReturn("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
102105
metaDataMap = new HashMap<String, Object>();

data-share/pom.xml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
<parent>
66
<groupId>io.mosip.datashare</groupId>
77
<artifactId>durian</artifactId>
8-
<version>1.3.0</version>
8+
<version>1.3.1-SNAPSHOT</version>
99
</parent>
1010

1111
<artifactId>data-share</artifactId>
12-
<version>1.3.0</version>
12+
<version>1.3.1-SNAPSHOT</version>
1313
<packaging>pom</packaging>
1414

1515
<name>data-share</name>
@@ -27,13 +27,13 @@
2727

2828
<spring-cloud-config.version>2.0.4.RELEASE</spring-cloud-config.version>
2929
<h2.version>1.4.197</h2.version>
30-
<kernel.core.version>1.3.0</kernel.core.version>
31-
<kernel.logger.logback.version>1.3.0</kernel.logger.logback.version>
30+
<kernel.core.version>1.3.1-SNAPSHOT</kernel.core.version>
31+
<kernel.logger.logback.version>1.3.1-SNAPSHOT</kernel.logger.logback.version>
3232
<kernel.crypto-jce.version>1.0.6</kernel.crypto-jce.version>
3333
<powermock.beta.version>2.0.7</powermock.beta.version>
3434
<mockito-core.version>3.11.2</mockito-core.version>
3535
<mockito.version>2.28.2</mockito.version>
36-
<kernel.object.store.version>1.3.0</kernel.object.store.version>
36+
<kernel.object.store.version>1.3.1-SNAPSHOT</kernel.object.store.version>
3737
<sonar.coverage.exclusions>**/constant/**,**/config/**,**/cache/**,**/dto/**,**/model/**,**/exception/**,**/repository/**,**/security/**,**/*Config.java,**/*Application.java,**/*Handler.java,**/*RestUtil.java</sonar.coverage.exclusions>
3838
<sonar.cpd.exclusions>**/dto/**,**/config/**,**/api/**</sonar.cpd.exclusions>
3939
<io.micrometer.prometheus.version>1.4.2</io.micrometer.prometheus.version>

0 commit comments

Comments
 (0)