-
Notifications
You must be signed in to change notification settings - Fork 85
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
Fix for incorrect error message in MergeableStoreViaGetPut #334
Changes from 1 commit
48fb060
3b08e8c
88353f2
c8e4533
3bfa49f
4c33634
56a1627
d99bfae
8009461
a3c15e1
e23d1f0
ecc4c04
8dbc0ba
fc41b3b
fa118b0
5253cac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,12 +16,13 @@ | |
|
||
package com.twitter.storehaus.algebra | ||
|
||
import com.twitter.algebird.{Semigroup, Monoid} | ||
import com.twitter.algebird.{Monoid, Semigroup} | ||
import com.twitter.bijection.ImplicitBijection | ||
import com.twitter.storehaus.{FutureOps, FutureCollector, Store} | ||
import com.twitter.util.Future | ||
|
||
import com.twitter.storehaus.{FutureCollector, FutureOps, Store} | ||
import com.twitter.util.{Future, Promise, Return, Throw, Try} | ||
import java.util.concurrent.atomic.{AtomicInteger, AtomicReferenceArray} | ||
import scala.language.implicitConversions | ||
import scala.reflect.ClassTag | ||
|
||
/** Main trait to represent stores that are used for aggregation */ | ||
trait MergeableStore[-K, V] extends Store[K, V] with Mergeable[K, V] | ||
|
@@ -31,25 +32,29 @@ object MergeableStore { | |
implicit def enrich[K, V](store: MergeableStore[K, V]): EnrichedMergeableStore[K, V] = | ||
new EnrichedMergeableStore(store) | ||
|
||
private[this] def addOpt[V](init: Option[V], inc: V)(implicit sg: Semigroup[V]): Option[V] = init match { | ||
case Some(i) => Some(sg.plus(i, inc)) | ||
case None => Some(inc) | ||
} | ||
|
||
/** | ||
* Implements multiMerge functionality in terms of an underlying | ||
* store's multiGet and multiSet. | ||
*/ | ||
def multiMergeFromMultiSet[K, V](store: Store[K, V], kvs: Map[K, V], | ||
missingfn: (K) => Future[Option[V]] = FutureOps.missingValueFor _) | ||
(implicit collect: FutureCollector, sg: Semigroup[V]): Map[K, Future[Option[V]]] = { | ||
def multiMergeFromMultiSet[K, V](store: Store[K, V], kvs: Map[K, V]) | ||
(implicit sg: Semigroup[V]): Map[K, Future[Option[V]]] = { | ||
val keySet = kvs.keySet | ||
val mGetResult: Seq[Future[(K, (Option[V], Option[V]))]] = | ||
store.multiGet(keySet).iterator.map { | ||
case (k, futureOptV) => | ||
futureOptV.map { init => | ||
val incV = kvs(k) | ||
val resV = init.map(Semigroup.plus(_, incV)).orElse(Some(incV)) | ||
k -> ((init, resV)) | ||
} | ||
}.toIndexedSeq | ||
val mGetResult: Map[K, Future[(Option[V], Option[V])]] = | ||
store.multiGet(keySet).map { case (k, futureOptV) => | ||
val newFOptV = futureOptV.map { oldV: Option[V] => | ||
val incV = kvs(k) | ||
val newV = addOpt(oldV, incV) | ||
(oldV, newV) | ||
} | ||
k -> newFOptV | ||
} | ||
|
||
val collectedMGetResult: Future[Seq[(K, (Option[V], Option[V]))]] = collect { mGetResult } | ||
val (collectedMGetResult, getFailures) = collectWithFailures(mGetResult) | ||
|
||
val mPutResultFut: Future[Map[K, Future[Option[V]]]] = | ||
collectedMGetResult.map { pairs: Seq[(K, (Option[V], Option[V]))] => | ||
|
@@ -61,10 +66,58 @@ object MergeableStore { | |
} | ||
|
||
/** | ||
* Combine original keys with result after put. Some keys might have dropped, | ||
* fill those using missingfn. | ||
* Combine original keys with result after put. | ||
*/ | ||
FutureOps.liftFutureValues(keySet, mPutResultFut, missingfn) | ||
FutureOps.liftFutureValues(keySet, mPutResultFut, getFailures) | ||
} | ||
|
||
/** | ||
* Collects keyed futures, partitioning out the failures. | ||
*/ | ||
private[algebra] def collectWithFailures[K, V](fs:Map[K, Future[V]]): (Future[Seq[(K, V)]], Map[K, Future[Nothing]]) = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we put spaces in types: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not crazy about using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this is to be private, it can even be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replaced with Array. |
||
if (fs.isEmpty) { | ||
(Future.value(Seq.empty[(K, V)]), Map.empty[K, Future[Nothing]]) | ||
} else { | ||
val fsSize = fs.size | ||
val results = new AtomicReferenceArray[Either[(K,Future[Nothing]), (K, V)]](fsSize) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what about There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
val countdown = new AtomicInteger(fsSize) | ||
val successCount = new AtomicInteger(0) | ||
val pSuccess = new Promise[Seq[(K, V)]] | ||
var failures = Map.empty[K, Future[Nothing]] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. doesn't this need to be an |
||
|
||
def collectResults() = { | ||
if (countdown.decrementAndGet() == 0) { | ||
val successArray = new Array[(K, V)](successCount.get()) | ||
var si = 0 | ||
var ri = 0 | ||
while (ri < fsSize) { | ||
results.get(ri) match { | ||
case Right(kv) => | ||
successArray(si) = kv | ||
si += 1 | ||
case Left(kv) => | ||
failures = failures + kv | ||
} | ||
ri += 1 | ||
} | ||
pSuccess.setValue(successArray) | ||
} | ||
} | ||
|
||
for (((k, f), i) <- fs.iterator.zipWithIndex) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if you are going to geek out on while loops above, doing the same here is probably worth it: val mapIt = fs.iterator
var i = 0
while(mapIt.hasNext) {
val kv = mapIt.next
fv._2.respond {
case Return(v) =>...
}
i += 0
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Implemented this, it does add a bit of complexity. Also does avoid one wrapping/unwraping for each item in the map. |
||
f respond { | ||
case Return(v) => | ||
results.set(i, Right(k -> v)) | ||
successCount.incrementAndGet() | ||
collectResults() | ||
case t@Throw(cause) => | ||
val failure = k -> Future.const(Throw(cause)) | ||
results.set(i, Left(failure)) | ||
collectResults() | ||
} | ||
} | ||
(pSuccess, failures) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. failures has to be a future right? You are returning a copy here, but the futures are not done yet. If you could have a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that was totally wrong. I've fixed it now and added tests as well. |
||
} | ||
} | ||
|
||
/** unpivot or uncurry this MergeableStore | ||
|
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.
Semigroup.maybePlus
this does this, but it always returnsV
notOption[V]
which I think is correct (you can just box inSome
as needed).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.
Makes sense. Don't want to merge latest algebird in this change. Is it ok to simplify this separate review where I can merge algebird. I've left a todo for now.