diff --git a/project/Build.scala b/project/Build.scala index 29b767f5..5b8eb316 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -118,7 +118,7 @@ object StorehausBuild extends Build { val algebirdVersion = "0.3.1" val bijectionVersion = "0.6.0" - val utilVersion = "6.3.7" + val utilVersion = "6.11.0" val scaldingVersion = "0.9.0rc4" lazy val storehaus = Project( diff --git a/storehaus-core/src/main/scala/com/twitter/storehaus/IterableStore.scala b/storehaus-core/src/main/scala/com/twitter/storehaus/IterableStore.scala new file mode 100644 index 00000000..cac36f82 --- /dev/null +++ b/storehaus-core/src/main/scala/com/twitter/storehaus/IterableStore.scala @@ -0,0 +1,57 @@ +/* + * Copyright 2013 Twitter Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.twitter.storehaus + +import com.twitter.util.{ Future, Promise, Return } +import com.twitter.concurrent.Spool +import com.twitter.concurrent.Spool.*:: + +object IterableStore { + /** Factory method to create a IterableStore from a Map. */ + def fromMap[K, V](m: Map[K, V]): IterableStore[K, V] = new MapStore(m) + + /** Helper method to convert Iterator to Spool. */ + def iteratorToSpool[K, V](it: Iterator[(K, V)]): Future[Spool[(K, V)]] = Future.value { + if (it.hasNext) + // *:: for lazy/deferred tail + it.next *:: iteratorToSpool(it) + else + Spool.empty + } +} +import IterableStore._ + +/** + * Trait for stores that allow iterating over their key-value pairs. + */ +trait IterableStore[+K, +V] { + + /** Returns a lazy Spool that wraps the store's keyspace. */ + def getAll: Future[Spool[(K, V)]] = + multiGetAll.flatMap { batchSpool => + batchSpool.flatMap { seq => + iteratorToSpool(seq.iterator) + } + } + + /** Batched version of getAll. Useful for paging stores. */ + def multiGetAll: Future[Spool[Seq[(K, V)]]] = + getAll.map { spool => + spool.map(Seq(_)) + } +} + diff --git a/storehaus-core/src/main/scala/com/twitter/storehaus/MapStore.scala b/storehaus-core/src/main/scala/com/twitter/storehaus/MapStore.scala index a9080116..036645a5 100644 --- a/storehaus-core/src/main/scala/com/twitter/storehaus/MapStore.scala +++ b/storehaus-core/src/main/scala/com/twitter/storehaus/MapStore.scala @@ -23,6 +23,9 @@ import com.twitter.util.Future * @author Oscar Boykin * @author Sam Ritchie */ -class MapStore[K, +V](val backingStore: Map[K, V] = Map[K, V]()) extends ReadableStore[K, V] { +class MapStore[K, +V](val backingStore: Map[K, V] = Map[K, V]()) extends ReadableStore[K, V] + with IterableStore[K, V] { override def get(k: K) = Future.value(backingStore.get(k)) + + override def getAll = IterableStore.iteratorToSpool(backingStore.iterator) } diff --git a/storehaus-core/src/test/scala/com/twitter/storehaus/IterableStoreProperties.scala b/storehaus-core/src/test/scala/com/twitter/storehaus/IterableStoreProperties.scala new file mode 100644 index 00000000..b76f307d --- /dev/null +++ b/storehaus-core/src/test/scala/com/twitter/storehaus/IterableStoreProperties.scala @@ -0,0 +1,37 @@ +/* + * Copyright 2013 Twitter Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.twitter.storehaus + +import com.twitter.util.{ Await, Future } + +import org.scalacheck.{ Arbitrary, Properties } +import org.scalacheck.Gen.choose +import org.scalacheck.Prop._ + +object IterableStoreProperties extends Properties("IterableStore") { + + def iterableStoreLaw[K: Arbitrary, V: Arbitrary](fn: Map[K, V] => IterableStore[K, V]) = + forAll { m: Map[K,V] => + val store = fn(m) + Await.result(store.getAll.flatMap { _.toSeq }).toMap == m + } + + property("MapStore obeys the IterableStore laws") = { + iterableStoreLaw[Int, String](IterableStore.fromMap(_)) + } +} +