-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CLEANUP: Refactored SMGetFuture. #685
Conversation
f380894
to
9a00017
Compare
This comment was marked as outdated.
This comment was marked as outdated.
저는 현재와 같은 구현 또는 PR의 구현 둘다 상관 없다고 생각합니다. 그래서 CompletableFuture를 사용하는 |
현재 api에 존재하는 missedKeyList, missedkeys, resultOperationStatus 등의 현재 PR의 방식
Future에서 field 인스턴스를 생성하는 경우
사실 사용자 입장에서는 리턴받은 Future에 공개 되는 메서드들이 적은게 좀 더 편리하다고 봅니다. |
SMGetResult라는 클래스를 만들어서 사용하는 방법도 있습니다. public class SMGetResult<T> {
public final int offset;
public final int count;
public final List<SMGetElement<T>> mergedResult = ...;
public final List<SMGetElement<T>> mergedSubList = ...;
public final List<OperationStatus> resultOperationStatus = ...;
public final List<OperationStatus> failedOperationStatus = ...;
public final List<SMGetTrimKey> mergedTrimmedKeys = ...;
public final List<String> missedKeyList = ...;
public final Map<String, CollectionOperationStatus> missedKeys = ...;
...
} private ... smget(...) {
final SMGetResult smgetResult = SMGetResult().builder()
.offset(offset)
.count(count)
.build();
...
@Override
public void gotMissedKey(byte[] data) {
smgetResult.missedKeyList.add(new String(data));
OperationStatus cause = new OperationStatus(false, "UNDEFINED");
smgetResult.missedKeys.put(new String(data), new CollectionOperationStatus(cause));
}
...
return SMGetFuture.builder()
.latch(blatch)
.ops(ops)
.timeout(operationTimeout)
.smgetResult(smgetResult)
.build()
} public class SMGetFuture<...> ... {
...
public ... get(long duration, TimeUnit units) {
...
if (ops.size() == 1) {
return smgetResult.mergedResult;
}
return smgetResult.getSubList();
}
...
public List<String> getMissedKeyList() {
return smgetResult.missedKeyList;
}
...
} |
@brido4125 @uhm0311 @uhm0311 |
@uhm0311 |
본 PR에 작업 진행하도록 하겠습니다. |
9a00017
to
a16f50a
Compare
b2c7acf
to
0c76bac
Compare
원본 코멘트 수정했습니다. 리뷰 부탁드립니다. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
리뷰 완료
} | ||
|
||
@Override | ||
public void gotTrimmedKey(String key, Object subkey) { | ||
if (failedOperationStatus.isEmpty()) { | ||
if (r.isFailedOperationStatusEmpty()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 경우를 보니,
예전 코드에 있는 stopCollect Flag를 따로 두어 처리하는 것이 나을 것 같습니다.
missedKeyList.add(key); | ||
missedKeys.put(key, new CollectionOperationStatus(cause)); | ||
r.addToMissedKeyList(key); | ||
r.putToMissedKeyMap(key, new CollectionOperationStatus(cause)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아래 하나의 메소드만 호출하고 해당 메소드 안에서 알아서 처리하면 좋겠습니다.
r.addMissedKey(key, new CollectionOperationStatus(cause));
} | ||
failedOperationStatus.add(status); | ||
r.addToFailedOperationStatus(status); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
r.addFailedOperationStatus(status)만 호출하고 나머진 호출된 메소드 안에서 모두 처리
return; | ||
} | ||
|
||
if (mergedResult.isEmpty()) { | ||
if (r.isMergedResultEmpty()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
r.mergeElementResult(eachResult) 만 호출하고 나머진 안에서 처리
if (mergedTrimmedKeys.isEmpty()) { | ||
mergedTrimmedKeys.addAll(eachTrimmedResult); | ||
if (r.isMergedTrimmedKeysEmpty()) { | ||
r.addToMergedTrimmedKeys(eachTrimmedResult); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eachTrimmedResult만 넘기고 merge 작업에 호출된 메소드 안에서 처리
pos += 1; | ||
} | ||
} | ||
} | ||
|
||
if (processedSMGetCount.get() == 0) { | ||
if (!mergedTrimmedKeys.isEmpty() && count <= mergedResult.size()) { | ||
if (!r.isMergedTrimmedKeysEmpty() && count <= r.sizeOfMergedTrimmedKeys()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
processedSMGetCount.get() == 0인 경우의 처리 로직도 result 객체의 메소드로 포장하여 호출
this.ops = ops; | ||
this.timeout = timeout; | ||
this.smGetResult = smGetResult; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this.result = result; 이면 좋겠습니다.
생성자의 인자는 ops, result, latch, timeout 순서가 좋습니다.
// merged result is empty, add all. | ||
mergedResult.addAll(eachResult); | ||
r.addToMergedResult(eachResult); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
old smget 쪽은 이에 해당하는 smgetResultOld를 따로 정의하는 것이 낫지 않나 생각합니다.
0c76bac
to
787e9db
Compare
Callback의 로직들을 SMGetResult 클래스로 이관했습니다. |
02f6d8e
to
2f11dd8
Compare
@brido4125 리뷰를 먼저 진행 바랍니다. |
...Result 형태의 객체를 다른 api에도 적용시키는 방안으로 결정을 하신건가요? |
2f11dd8
to
c2b0997
Compare
아직 결정한 건 아니고, 이에 대한 의견을 먼저 받았으면 합니다. 그리고, 이러한 결정과 무관하게 smget 리팩토링을 리뷰해 주면 될 것 같습니다.
|
@oliviarla 본 PR 리뷰할 수 있을까요? |
@jhpark816 넵 진행해보겠습니다. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
리뷰 완료했습니다.
|
||
// if processedSMGetCount is 0, then all smget is done. | ||
final AtomicInteger processedSMGetCount = new AtomicInteger(smGetList.size()); | ||
final AtomicBoolean mergedTrim = new AtomicBoolean(false); | ||
final AtomicBoolean stopCollect = new AtomicBoolean(false); | ||
|
||
final CollectionOperationStatus missedKeyCause |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gotMissedKey 메서드에서만 사용되는 것 같은데 변수로 따로 두신 이유가 궁금합니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gotMissedKey가 호출될 때마다 매번 객체를 새로 생성할 필요가 없어서 하나 만들어두고 쓰기 위해 두었습니다.
import net.spy.memcached.ops.OperationStatus; | ||
|
||
public class SMGetResult<T> { | ||
private final int offset; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
offset을 활용하는 방식이 Deprecated된 것 같은데, SMGetResult 클래스에는 offset을 제외한 최신 사항만 반영해두는게 나중에 제거하기 편하지 않을까요?
혹은 Deprecated된 부분은 리팩토링에서 제외하거나, Result 클래스를 두개로 분리할 수도 있을 것 같습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SMGetResult 클래스를 추상 클래스로 두고, SMGetResultImpl 클래스와 SMGetResultOldImpl 클래스로 분리했습니다.
c2b0997
to
d4488ea
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
일부 리뷰
final String TRIMMED = "TRIMMED"; | ||
final String DUPLICATED = "DUPLICATED"; | ||
final String DUPLICATED_TRIMMED = "DUPLICATED_TRIMMED"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
END, DUPLICATED 사용되지 않아 제거하였네요.
TRIMMED, DUPLICATED_TRIMMED도 해당 코드에서 문자열을 직접 사용하고, 변수는 제거하도록 합시다.
이 부분은 CLEANUP 작업으로 별도 PR로 받는 것이 좋을 것 같습니다.
missedKeyList.add(new String(data)); | ||
OperationStatus cause = new OperationStatus(false, "UNDEFINED"); | ||
missedKeys.put(new String(data), new CollectionOperationStatus(cause)); | ||
result.addMissedKey(new String(data), missedKeyCause); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missedKeyCause 멤버를 두는 대신에 기존처럼 아래와 같이 구현하면 좋겠습니다.
수행 효율은 떨어지지만 코드 읽기가 훨씬 수월합니다.
OperationStatus cause = new OperationStatus(false, "UNDEFINED");
result.addMissedKey(new String(data), new CollectionOperationStatus(cause));
} | ||
} | ||
} | ||
result.mergeSMGetElements(eachResult, isTrimmed, mergedTrim, processedSMGetCount, reverse); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
대략 아래와 같은 로직으로 구현하면 좋겠습니다.
- smget 수행이 성공이면,
- merge 수행
- smget 수행의 마지막이면, 최종 operation status 생성
- smget 수행이 실패이면, (아래 로직이 맞는 지는 확인 필요)
- 실패 처리
if (status.isSuccess()) {
result.mergeSMGetElements(eachResult, isTrimmed);
if (processedSMGetCount.get() == 0) {
result.makeResultOperationsStatus();
}
} else {
if (!stopCollect.get()) {
result.addFailedOperationStatus();
stopCollect.set(true);
}
}
결국
- mergedTrim도 SMGetResult 안에서 선언해야 하고,
- processedSMGetCount는 넘기지 않도록 하고,
- reverse도 SMGetResult 생성 시에 넘기면 좋을 것 같습니다.
} | ||
} | ||
} | ||
result.mergeSMGetElements(eachResult, eachTrimmedResult, processedSMGetCount, reverse, smgetMode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기도 위의 코멘트 참고하여 같은 형태로 변경하면 좋겠습니다.
d4488ea
to
77031da
Compare
리뷰 반영했습니다. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
일부 리뷰
} | ||
if (status.isSuccess()) { | ||
boolean isTrimmed = (TRIMMED.equals(status.getMessage()) || | ||
DUPLICATED_TRIMMED.equals(status.getMessage())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아래와 같이 수정하고, 위에 선언된 String 변수들을 모두 제거하면 될 것 같습니다.
boolean isTrimmed = ("TRIMMED".equals(status.getMessage()) ||
"DUPLICATED_TRIMMED".equals(status.getMessage()));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
반영했습니다.
= Collections.synchronizedList(new ArrayList<OperationStatus>(1)); | ||
final List<OperationStatus> failedOperationStatus | ||
= Collections.synchronizedList(new ArrayList<OperationStatus>(1)); | ||
final SMGetResultImpl<T> result = new SMGetResultImpl<T>(count, reverse, smgetMode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
smget 연산의 argument가 아래와 같습니다.
- 기존 : offset, count
- 신규: count, smgetMode
이를 고려하여, count, smgetMode, reverse 순서이면 좋겠습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
반영했습니다.
} | ||
|
||
return mergedSubList; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아래 메소드와 인자를 두고 subList 구하는 것이 의미에 나을 것 같습니다.
List<SMGetElement<T>> getSubList(int offset, int count)
Implementation에서 getSubList(offset, count)
로 호출하거나 getSubList(0, count)
로 호출하고요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 메소드를 다시 생각해 보니,
offset == 0인 경우는 getSubList 수행하지 않고 mergedResult를 그대로 리턴해도 되지 않나요?
즉, mergedResult.size() <= count
조건을 항상 만족할 것 같습니다. 어떤가요?
위 사항이 맞으면, getSubList()는 OldImpl에만 두면 될 것 같습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아래 메소드와 인자를 두고 subList 구하는 것이 의미에 나을 것 같습니다.
그러면 SMGetFuture에 int offset, int count 필드가 필요합니다.
저는 이것이 좋은 변경이라고 생각하지 않습니다.
이 메소드를 다시 생각해 보니,
offset == 0인 경우는 getSubList 수행하지 않고 mergedResult를 그대로 리턴해도 되지 않나요?
즉, mergedResult.size() <= count 조건을 항상 만족할 것 같습니다. 어떤가요?
3개의 노드로부터 smget을 count=50을 주어 실행하여 받은 결과를 합치면 최대 150개이지 않나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
3개의 노드로부터 smget을 count=50을 주어 실행하여 받은 결과를 합치면 최대 150개이지 않나요?
count가 50이면 최종 50개의 결과만 있으면 되므로,
mergedResult에는 항상 최대 50개의 결과만 보관하는 형태로 구현했을 것으로 생각됩니다.
구현 로직을 한번 검토해 주시죠.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (mergedResult.size() > count) {
// Remove elements that exceed the requested count.
mergedResult.remove(count);
}
Server 오류로 인해 주어진 count 보다 더 많은 응답을 반환하지만 않으면 맞습니다.
다만 위 코드의 if 부분을 while로 바꾸고 난 다음에 subList()의 구현을 바꾸는 것이 더 안전하다고 보는데, 어떤가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그러면 SMGetFuture에 int offset, int count 필드가 필요합니다.
저는 이것이 좋은 변경이라고 생각하지 않습니다.
SMGetFuture에 offset, count 필드를 두자는 것은 아니며,
구체적 예를 들면, SMGetResultImpl과 SMGetResultOldImpl에 아래의 메소드를 두고,
SMGetFuture에서 getFinalResult() 호출하자는 것입니다.
// SMGetResultImpl
public List<SMGetElement<T>> getFinalResult() {
return mergedResult;
}
// SMGETResultOldImpl
public List<SMGetElement<T>> getFinalResult() {
if (offset > 0 && mergedCount > 1) {
return mergedResult.subList(offset, count);
}
return mergedResult;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
List<SMGetElement<T>> getSubList(int offset, int count)
위 형태의 메소드를 구현하는 것인 줄 알았습니다.
다만 위 코드의 if 부분을 while로 바꾸고 난 다음에 subList()의 구현을 바꾸는 것이 더 안전하다고 보는데, 어떤가요?
이 부분을 반영해도 될까요?
public SMGetResultImpl(int count, boolean reverse, SMGetMode smGetMode) { | ||
super(count, count, reverse); | ||
|
||
this.smGetMode = smGetMode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아래와 같이 count, smgetMode, reverse 데이터가 실제 사용되는 Impl 클래스에 두는 것이 읽기에 좋은 데, 어떤가요?
super(count);
this.count = count;
this.smGetMode = smGetMode;
this.reverse = reverse;
OldImpl 경우는 아래 로직의 생성자를 가지도록 하고요.
super(offset + count);
this.offset = offset;
this.count = count;
this.reverse = reverse;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
공통 부분을 super 클래스에 두는 것은 흔한 방식입니다.
이클립스나 인텔리제이를 쓰면 this.
을 입력했을 때 무엇을 참조 가능한지도 볼 수 있기 때문에 불필요하다고 봅니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
old smget에서는 <offset, count>
쌍이 하나의 의미를 가지고,
new smget에서는 <count, smGetMode>
쌍이 하나의 의미를 가집니다.
(new smget은 향후 <count, unique>
쌍으로 변경할 예정)
이와 같이 쌍으로 표현되는 의미가 SMGetResult에 같이 표현되는 것이 아니라
단순히 중복되는 변수이다 아니다로 구분하여 super에 두는 것이
의미(semantic)을 살리지 못한 구현이라서 코멘트한 것입니다.
a152bdd
to
6a18e79
Compare
리뷰 반영했습니다. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
리뷰 완료
} | ||
result.clearMergedResult(); | ||
getLogger().warn("SMGetFailed. status=%s", status); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아래와 같이 new smget와 같은 로직을 가지는 것이 더 맞지 않나 생각합니다.
stopCollect.set(true);
result.addFailedOperationStatus(status);
getLogger().warn("SMGetFailed. status=%s", status);
public void addFailedOperationStatus(OperationStatus status) { | ||
failedOperationStatus.add(status); | ||
mergedResult.clear(); | ||
mergedTrimmedKeys.clear(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아래와 같이 failedOperationStatus가 처음 등록될 경우만 mergedResult를 clear해도 되지만,
현재 코드와 같이 무조건 clear 하는 것이 괜찮은 것 같습니다. 참고로 적은 사항입니다.
if (failedOperationStatus.size() == 0) {
mergedResult.clear();
mergedTrimmedKeys.clear();
}
failedOperationStatus.add(status);
|
||
public void clearMergedResult() { | ||
mergedResult.clear(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 메소드는 제거하고 addFailedOperationStatus()
에서
mergedResult.clear();
호출합시다.
mergedResult.add(pos, result); | ||
while (mergedResult.size() > totalResultElementCount) { | ||
mergedResult.remove(totalResultElementCount); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기 코드를 확인해 보니, 아래 코드에 의해
mergedResult 크기는 totalResultElementCount 초과할 수 없습니다.
따라서, if 문이어도 충분합니다.
if (pos >= totalResultElementCount) {
addAll = false;
break;
}
오히려, 아래 코드에서 주어진 eachResult 크기가 totalResultElementCount 이하임을 보장할 수 없으니,
while 문으로 totalResultElementCount 초과 개수를 제거하는 코드가 들어가는 것이 안전할 것 같습니다.
if (mergedResult.isEmpty()) {
// merged result is empty, add all.
mergedResult.addAll(eachResult);
mergedTrim.set(isTrimmed);
} else { | ||
finalResult = mergedResult; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
totalResultElementCount < mergedResult.size()
조건을 만족하는 경우는 없으니, 제거해도 될 것 같습니다.
6a18e79
to
0d3b519
Compare
리뷰 반영했습니다. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
리뷰 완료
} | ||
|
||
public void mergeSMGetElements(final List<SMGetElement<T>> eachResult, | ||
final List<SMGetTrimKey> eachTrimmedResult) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mergeSMGetElements 코드가 기존 코드와 동일한 지를 확인 바랍니다.
차이가 있어 보입니다.
Old Impl 쪽도 확인해 주시고요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (processedSMGetCount.get() == 0) {
이 부분이 없는 걸 말씀하시는 건가요?
#685 (comment)
이 코멘트에 따라 mergeSMGetElements()에서 제거하고 다른 곳으로 옮겼습니다.
0d3b519
to
3f66924
Compare
|
||
@Override | ||
public void makeResultOperationStatus() { | ||
if (!mergedTrimmedKeys.isEmpty() && count <= mergedTrimmedKeys.size()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 코드가 기존 코드와 다른 것 같습니다.
그 외에도 차이가 있는 부분이 있는 지를 확인 바랍니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아. mergeSMGetElements() 메소드가 아니라 makeResultOperationStatus() 메소드였습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
반영했습니다.
3f66924
to
539d4c0
Compare
539d4c0
to
70d3cfd
Compare
SMGetResult 클래스의 위치를 별도의 패키지로 옮겼습니다. |
SMGetFuture를 추상 클래스에서 일반 클래스로 변경했습니다.
일반 클래스로 변경하면서 각 smget 메소드에서 구현했던 추상 메소드를 SMGetFuture 내로 옮겼습니다.
SMGetFuture를 통해 접근 가능한 여러 필드들은 SMGetResult 클래스를 만들어 접근하도록 했습니다.
SMGetResult 객체에 직접 참조를 통해 필드에 Write 하게 했는데, 간접 참조로 바꿔야 더 명확하다면 간접 참조로 바꾸겠습니다.
r.addToMergedResult(eachResult);
r.getMergedResult().addAll(eachResult)
SMGetFuture 내의 필드로
SMGetResult<?> smGetResult
를 두었는데,SMGetResult<T>
가 아닌 이유는 smget 메소드의 return type이SMGetFuture<List<SMGetElement<G>>>
이기 때문입니다.class SMGetFuture<T>
에서T = List<SMGetElement<G>>
로부터 내부의<G>
를 가져올 수 없어SMGetResult<?>
형태의 필드를 둔 것입니다.추가 기대 효과
또한 현재 형태의 리팩토링은 Future 클래스에서 public으로 노출하는 메소드를 getter에 한정하게 되어 다음의 이슈를 해결할 수 있는 방안 중 하나가 될 것 같습니다.