patternjavaMinor
Blocking reads when writes are happening on two flows
Viewed 0 times
happeningwritesareblockingreadstwowhenflows
Problem
I am trying to implement lock by which I want to avoid reads from happening whenever I am doing a write.
My requirements are:
As I have three Maps -
Now I have two flows as shown below: For each flow, I have a URL from where we get the data for above three maps. In general, I will have three maps for both the flow so we will have different values for those three maps for PROCESS flow as compared to DEVICE flow.
I have a background thread running every 5 minutes which gets the data from each FLOW url and populate those three maps whenever there is an update. When the application is started for the first time, then it will update the mapping and after that, it will update the mapping 7-8 months afterwards. And it doesn't mean that both the flow mapping will change at the same time. It might be possible PROCESS mapping has change but not DEVICE mapping.
```
public class DataScheduler {
private RestTemplate restTemplate = new RestTemplate();
private static final Str
My requirements are:
- Reads block until all three maps have been set for the first time.
- Now second time, If I am updating the maps, I can still return all the three old maps value(before the updates are done on all three maps) or it should block and return me all the new three maps value whenever the updates are done on all the three maps.
As I have three Maps -
primaryMapping , secondaryMapping and tertiaryMapping so it should return either all the new values of three updated maps or it should return all the old values of the map. Basically, while updating I don't want to return primaryMapping having old values, secondaryMapping having having new values, and tertiaryMapping with new values. It should be consistent, either it should return old values or it should return new values after updating the maps. In my case, updating of maps will happen once in 7 or 8 months (very rarely). I am using Countdown Latch for this and it is working fine so far without any issues.Now I have two flows as shown below: For each flow, I have a URL from where we get the data for above three maps. In general, I will have three maps for both the flow so we will have different values for those three maps for PROCESS flow as compared to DEVICE flow.
public enum FlowType {
PROCESS, DEVICE;
}I have a background thread running every 5 minutes which gets the data from each FLOW url and populate those three maps whenever there is an update. When the application is started for the first time, then it will update the mapping and after that, it will update the mapping 7-8 months afterwards. And it doesn't mean that both the flow mapping will change at the same time. It might be possible PROCESS mapping has change but not DEVICE mapping.
```
public class DataScheduler {
private RestTemplate restTemplate = new RestTemplate();
private static final Str
Solution
DataSchedulerSince
FlowType is an enum, I will suggest using an EnumMap inside callService(), plus inlining your variables:public void callService() throws Exception {
// note: no need for early, redundant declarations when you can inline
// String url = null;
Map holder = new EnumMap<>(FlowType.class);
for (FlowType flow : FlowType.values()) {
try {
// inline-d the following
// url = getURL(flow);
// String response = restTemplate.getForObject(url, String.class);
holder.put(flow, restTemplate.getForObject(getURL(flow), String.class));
} catch (RestClientException ex) {
// logging exception here using logger
}
}
parseResponse(holder);
}Is it possible to use more specific
Exceptions to throw from parseResponse()? They may influence how callers of this method can recover from such errors, e.g. just log, do a timeout-and-retry, or to hit the big-red "Panic!!!" button.You can also consider re-working the
if-statements a little bit to reduce the nesting, and also to cut back on the variable declarations again...for (Map.Entry responseEntry : responses.entrySet()) {
FlowType flow = responseEntry.getKey();
String response = responseEntry.getValue();
if (DataUtils.isEmpty(response)) {
continue;
}
try (Scanner scanner = new Scanner(response)) {
if (Boolean.parseBoolean(scanner.nextLine().trim().substring(HAS_PROCESS_LEN))) {
update = true;
// some code
// BTW is 'version' some kind of constant?
// if so, then name it VERSION perhaps?
partitionMapper.put(flow, PartitionHolder.createMapping(
new HashMap<>(), new HashMap<>(), new HashMap<>(), version));
}
}
}PartitionHolderThe implementation for local/remote primary/secondary addresses all look extremely familiar... to the refactoring machine!
public String getLocalPrimaryAddress(final String localDataCenterPath,
final int partitionId) {
return lookup(primaryMapping, localDataCenterPath, partitionId);
}
public String getRemotePrimaryAddress(final String remoteDataCenterPath,
final int partitionId) {
return lookup(primaryMapping, remoteDataCenterPath, partitionId);
}
public String getLocalSecondaryHostIPAddress(final String localDataCenterPath,
final int partitionId) {
return lookup(secondaryMapping, localDataCenterPath, partitionId);
}
public String getRemoteSecondaryHostIPAddress(final String remoteDataCenterPath,
final int partitionId) {
return lookup(secondaryMapping, remoteDataCenterPath, partitionId);
}
private String lookup(final Map> mapping,
final String path, final int partitionId) {
return getHostname(path, mapping.get(path).get(partitionId));
}
private String getHostname(final String dataCenterPath, final int hostId) {
return tertiaryMapping.get(dataCenterPath).get(hostId);
}Instead of duplicating the code, you can introduce a
private method lookup(Map, String, int) that accepts the relevant Map, the path and the partitionId to standardize the lookup code. Again, instead of using a temporary variable final String hostname = ... in getHostname(String, int), it will be shorter to simply return the result of performing the get() operation.Also, as you should know, Java 7 supports type inference for generic instance creation aka diamond operator, so you should use
<> consistently, where applicable.Code Snippets
public void callService() throws Exception {
// note: no need for early, redundant declarations when you can inline
// String url = null;
Map<FlowType, String> holder = new EnumMap<>(FlowType.class);
for (FlowType flow : FlowType.values()) {
try {
// inline-d the following
// url = getURL(flow);
// String response = restTemplate.getForObject(url, String.class);
holder.put(flow, restTemplate.getForObject(getURL(flow), String.class));
} catch (RestClientException ex) {
// logging exception here using logger
}
}
parseResponse(holder);
}for (Map.Entry<FlowType, String> responseEntry : responses.entrySet()) {
FlowType flow = responseEntry.getKey();
String response = responseEntry.getValue();
if (DataUtils.isEmpty(response)) {
continue;
}
try (Scanner scanner = new Scanner(response)) {
if (Boolean.parseBoolean(scanner.nextLine().trim().substring(HAS_PROCESS_LEN))) {
update = true;
// some code
// BTW is 'version' some kind of constant?
// if so, then name it VERSION perhaps?
partitionMapper.put(flow, PartitionHolder.createMapping(
new HashMap<>(), new HashMap<>(), new HashMap<>(), version));
}
}
}public String getLocalPrimaryAddress(final String localDataCenterPath,
final int partitionId) {
return lookup(primaryMapping, localDataCenterPath, partitionId);
}
public String getRemotePrimaryAddress(final String remoteDataCenterPath,
final int partitionId) {
return lookup(primaryMapping, remoteDataCenterPath, partitionId);
}
public String getLocalSecondaryHostIPAddress(final String localDataCenterPath,
final int partitionId) {
return lookup(secondaryMapping, localDataCenterPath, partitionId);
}
public String getRemoteSecondaryHostIPAddress(final String remoteDataCenterPath,
final int partitionId) {
return lookup(secondaryMapping, remoteDataCenterPath, partitionId);
}
private String lookup(final Map<String, Map<Integer, Integer>> mapping,
final String path, final int partitionId) {
return getHostname(path, mapping.get(path).get(partitionId));
}
private String getHostname(final String dataCenterPath, final int hostId) {
return tertiaryMapping.get(dataCenterPath).get(hostId);
}Context
StackExchange Code Review Q#95028, answer score: 3
Revisions (0)
No revisions yet.