killbill-aplcache

usage: meter: split usage and metering code The usage module

11/30/2012 5:34:54 PM

Changes

meter/pom.xml 109(+109 -0)

meter/README.md 5(+5 -0)

pom.xml 6(+6 -0)

server/pom.xml 8(+8 -0)

usage/pom.xml 16(+0 -16)

usage/README.md 5(+5 -0)

Details

diff --git a/api/src/main/java/com/ning/billing/meter/api/MeterUserApi.java b/api/src/main/java/com/ning/billing/meter/api/MeterUserApi.java
new file mode 100644
index 0000000..5869d67
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/meter/api/MeterUserApi.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you 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.ning.billing.meter.api;
+
+import java.util.Map;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.util.callcontext.CallContext;
+
+public interface MeterUserApi {
+
+    /**
+     * Shortcut API to record a usage value of "1" for a given category and metric.
+     *
+     * @param bundleId     bundle id source
+     * @param categoryName category name for this usage
+     * @param metricName   metric name associated with this category
+     * @param context      call context
+     */
+    public void incrementUsage(UUID bundleId, String categoryName, String metricName, DateTime timestamp, CallContext context);
+
+    /**
+     * Shortcut API to record a usage value of "1" for a given category and metric.
+     * <p/>
+     * This will also store an aggregation of all usage data across all metrics for that category.
+     * This is useful if one wants to store fine grained usage information (e.g. number of minutes used per cell phone number),
+     * while keeping a fast access path to the aggregate (e.g. number of minutes used across all cell phone numbers).
+     *
+     * @param bundleId     bundle id source
+     * @param categoryName category name for this usage
+     * @param metricName   metric name associated with this category
+     * @param context      call context
+     */
+    public void incrementUsageAndAggregate(UUID bundleId, String categoryName, String metricName, DateTime timestamp, CallContext context);
+
+    /**
+     * Fine grained usage API. This is used to record e.g. "X has used 2 credits at 2012/02/04 4:12pm".
+     *
+     * @param bundleId                       bundle id source
+     * @param samplesForCategoriesAndMetrics samples per metric and category
+     * @param timestamp                      timestamp of this usage
+     * @param context                        tenant context
+     */
+    public void incrementUsage(UUID bundleId, Map<String, Map<String, Object>> samplesForCategoriesAndMetrics, DateTime timestamp, CallContext context);
+}
diff --git a/api/src/main/java/com/ning/billing/usage/api/RolledUpUsage.java b/api/src/main/java/com/ning/billing/usage/api/RolledUpUsage.java
new file mode 100644
index 0000000..8774b71
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/usage/api/RolledUpUsage.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you 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.ning.billing.usage.api;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+public interface RolledUpUsage {
+
+    UUID getSubscriptionId();
+
+    String getUnitType();
+
+    DateTime getStartTime();
+
+    DateTime getEndTime();
+
+    BigDecimal getAmount();
+}
diff --git a/api/src/main/java/com/ning/billing/usage/api/UsageUserApi.java b/api/src/main/java/com/ning/billing/usage/api/UsageUserApi.java
index 8735313..296b58a 100644
--- a/api/src/main/java/com/ning/billing/usage/api/UsageUserApi.java
+++ b/api/src/main/java/com/ning/billing/usage/api/UsageUserApi.java
@@ -16,46 +16,37 @@
 
 package com.ning.billing.usage.api;
 
+import java.math.BigDecimal;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
 
-import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.TenantContext;
 
 public interface UsageUserApi {
 
     /**
-     * Shortcut API to record a usage value of "1" for a given metric.
+     * Bulk usage API when the external system (or the meter module) rolls-up usage data.
+     * <p/>
+     * This is used to record e.g. "X has used 12 minutes of his data plan between 2012/02/04 and 2012/02/06".
      *
-     * @param bundleId   bundle id source
-     * @param metricName metric name for this usage
-     * @param context    call context
+     * @param subscriptionId subscription id source
+     * @param unitType       unit type for this usage
+     * @param startTime      start date of the usage period
+     * @param endTime        end date of the usage period
+     * @param amount         value to record
+     * @param context        tenant context
      */
-    public void incrementUsage(UUID bundleId, String metricName, CallContext context) throws EntitlementUserApiException;
+    public void recordRolledUpUsage(UUID subscriptionId, String unitType, DateTime startTime, DateTime endTime,
+                                    BigDecimal amount, CallContext context);
 
     /**
-     * Fine grained usage API if the external system doesn't roll its usage data. This is used to record e.g. "X has used
-     * 2 credits from his plan at 2012/02/04 4:12pm".
+     * Get usage information for a given subscription.
      *
-     * @param bundleId   bundle id source
-     * @param metricName metric name for this usage
-     * @param timestamp  timestamp of this usage
-     * @param value      value to record
-     * @param context    tenant context
+     * @param subscriptionId subscription id
+     * @param context        tenant context
+     * @return usage data (rolled-up)
      */
-    public void recordUsage(UUID bundleId, String metricName, DateTime timestamp, long value, CallContext context) throws EntitlementUserApiException;
-
-    /**
-     * Bulk usage API if the external system rolls-up usage data. This is used to record e.g. "X has used 12 minutes
-     * of his data plan between 2012/02/04 and 2012/02/06".
-     *
-     * @param bundleId   bundle id source
-     * @param metricName metric name for this usage
-     * @param startDate  start date of the usage period
-     * @param endDate    end date of the usage period
-     * @param value      value to record
-     * @param context    tenant context
-     */
-    public void recordRolledUpUsage(UUID bundleId, String metricName, DateTime startDate, DateTime endDate, long value, CallContext context) throws EntitlementUserApiException;
+    public RolledUpUsage getUsageForSubscription(UUID subscriptionId, TenantContext context);
 }

meter/pom.xml 109(+109 -0)

diff --git a/meter/pom.xml b/meter/pom.xml
new file mode 100644
index 0000000..f9d3a62
--- /dev/null
+++ b/meter/pom.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- ~ Copyright 2010-2011 Ning, Inc. ~ ~ Ning licenses this file to you
+    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. -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.ning.billing</groupId>
+        <artifactId>killbill</artifactId>
+        <version>0.1.45-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <artifactId>killbill-meter</artifactId>
+    <name>killbill-meter</name>
+    <packaging>jar</packaging>
+    <dependencies>
+        <dependency>
+            <groupId>org.jdbi</groupId>
+            <artifactId>jdbi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-joda</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.dataformat</groupId>
+            <artifactId>jackson-dataformat-smile</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.util</groupId>
+            <artifactId>low-gc-membuffers</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.inject</groupId>
+            <artifactId>guice</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-util</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>joda-time</groupId>
+            <artifactId>joda-time</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.skife.config</groupId>
+            <artifactId>config-magic</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.antlr</groupId>
+            <artifactId>stringtemplate</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-mxj</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-mxj-db-files</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>

meter/README.md 5(+5 -0)

diff --git a/meter/README.md b/meter/README.md
new file mode 100644
index 0000000..c2b0c54
--- /dev/null
+++ b/meter/README.md
@@ -0,0 +1,5 @@
+Meter
+-----
+
+The meter modules takes care of aggregating usage information (e.g. rolling up individual pageviews to a website into daily or monthly aggregates).
+See the usage module for the billing aspect.
diff --git a/meter/src/main/java/com/ning/billing/meter/api/user/DefaultMeterUserApi.java b/meter/src/main/java/com/ning/billing/meter/api/user/DefaultMeterUserApi.java
new file mode 100644
index 0000000..28024af
--- /dev/null
+++ b/meter/src/main/java/com/ning/billing/meter/api/user/DefaultMeterUserApi.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you 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.ning.billing.meter.api.user;
+
+import java.util.Map;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.ObjectType;
+import com.ning.billing.meter.api.MeterUserApi;
+import com.ning.billing.meter.timeline.TimelineEventHandler;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.callcontext.InternalCallContextFactory;
+
+import com.google.common.collect.ImmutableMap;
+
+public class DefaultMeterUserApi implements MeterUserApi {
+
+    private static final String AGGREGATE_METRIC_NAME = "__AGGREGATE__";
+
+    private final TimelineEventHandler timelineEventHandler;
+    private final InternalCallContextFactory internalCallContextFactory;
+
+    @Inject
+    public DefaultMeterUserApi(final TimelineEventHandler timelineEventHandler,
+                               final InternalCallContextFactory internalCallContextFactory) {
+        this.timelineEventHandler = timelineEventHandler;
+        this.internalCallContextFactory = internalCallContextFactory;
+    }
+
+    @Override
+    public void incrementUsage(final UUID bundleId, final String categoryName, final String metricName,
+                               final DateTime timestamp, final CallContext context) {
+        incrementUsage(bundleId,
+                       ImmutableMap.<String, Map<String, Object>>of(categoryName, ImmutableMap.<String, Object>of(metricName, (short) 1)),
+                       timestamp,
+                       context);
+    }
+
+    @Override
+    public void incrementUsageAndAggregate(final UUID bundleId, final String categoryName, final String metricName,
+                                           final DateTime timestamp, final CallContext context) {
+        incrementUsage(bundleId,
+                       ImmutableMap.<String, Map<String, Object>>of(categoryName, ImmutableMap.<String, Object>of(metricName, (short) 1,
+                                                                                                                  AGGREGATE_METRIC_NAME, (short) 1)),
+                       timestamp,
+                       context);
+    }
+
+    @Override
+    public void incrementUsage(final UUID bundleId, final Map<String, Map<String, Object>> samplesForCategoriesAndMetrics,
+                               final DateTime timestamp, final CallContext context) {
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(bundleId, ObjectType.BUNDLE, context);
+
+        for (final String category : samplesForCategoriesAndMetrics.keySet()) {
+            final String sourceName = bundleId.toString();
+            timelineEventHandler.record(sourceName, category, timestamp, samplesForCategoriesAndMetrics.get(category),
+                                        internalCallContext);
+        }
+    }
+}
diff --git a/meter/src/main/java/com/ning/billing/meter/glue/MeterModule.java b/meter/src/main/java/com/ning/billing/meter/glue/MeterModule.java
new file mode 100644
index 0000000..5eef3b9
--- /dev/null
+++ b/meter/src/main/java/com/ning/billing/meter/glue/MeterModule.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you 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.ning.billing.meter.glue;
+
+import java.io.IOException;
+
+import org.skife.config.ConfigSource;
+import org.skife.config.ConfigurationObjectFactory;
+import org.skife.config.SimplePropertyConfigSource;
+
+import com.ning.billing.meter.timeline.codec.DefaultSampleCoder;
+import com.ning.billing.meter.timeline.codec.SampleCoder;
+import com.ning.billing.meter.timeline.persistent.FileBackedBuffer;
+import com.ning.billing.meter.timeline.persistent.TimelineDao;
+import com.ning.billing.meter.timeline.times.DefaultTimelineCoder;
+import com.ning.billing.meter.timeline.times.TimelineCoder;
+import com.ning.billing.util.config.MeterConfig;
+
+import com.google.inject.AbstractModule;
+
+public class MeterModule extends AbstractModule {
+
+    private final ConfigSource configSource;
+
+    public MeterModule() {
+        this(new SimplePropertyConfigSource(System.getProperties()));
+    }
+
+    public MeterModule(final ConfigSource configSource) {
+        this.configSource = configSource;
+    }
+
+    protected MeterConfig installConfig() {
+        final MeterConfig config = new ConfigurationObjectFactory(configSource).build(MeterConfig.class);
+        bind(MeterConfig.class).toInstance(config);
+
+        return config;
+    }
+
+    protected void configureDao() {
+        bind(TimelineDao.class).toProvider(CachingDefaultTimelineDaoProvider.class).asEagerSingleton();
+    }
+
+    protected void configureTimelineObjects() {
+        bind(TimelineCoder.class).to(DefaultTimelineCoder.class).asEagerSingleton();
+        bind(SampleCoder.class).to(DefaultSampleCoder.class).asEagerSingleton();
+    }
+
+    protected void configureFileBackedBuffer(final MeterConfig config) {
+        // Persistent buffer for in-memory samples
+        try {
+            final boolean deleteFilesOnClose = config.getShutdownSaveMode().equals("save_all_timelines");
+            final FileBackedBuffer fileBackedBuffer = new FileBackedBuffer(config.getSpoolDir(), "TimelineEventHandler", deleteFilesOnClose, config.getSegmentsSize(), config.getMaxNbSegments());
+            bind(FileBackedBuffer.class).toInstance(fileBackedBuffer);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    protected void configure() {
+
+        final MeterConfig config = installConfig();
+
+        configureFileBackedBuffer(config);
+        configureDao();
+        configureTimelineObjects();
+
+        // TODO
+        //configureTimelineAggregator();
+        //configureBackgroundDBChunkWriter();
+        //configureReplayer();
+    }
+}
diff --git a/meter/src/main/resources/com/ning/billing/meter/timeline/ddl.sql b/meter/src/main/resources/com/ning/billing/meter/timeline/ddl.sql
new file mode 100644
index 0000000..23f046d
--- /dev/null
+++ b/meter/src/main/resources/com/ning/billing/meter/timeline/ddl.sql
@@ -0,0 +1,71 @@
+create table sources (
+  record_id int(11) unsigned not null auto_increment
+, bundle_id char(36) default null
+, subscription_id char(36) default null
+, created_date datetime default null
+, created_by varchar(50) default null
+, updated_date datetime default null
+, updated_by varchar(50) default null
+, account_record_id int(11) unsigned default null
+, tenant_record_id int(11) unsigned default null
+, primary key(record_id)
+, index created_date_record_id_dx (created_date, record_id)
+) engine = innodb default charset = latin1;
+create index sources_tenant_account_record_id on sources(tenant_record_id, account_record_id);
+
+create table event_categories (
+  record_id integer not null auto_increment
+, event_category varchar(256) not null
+, tenant_record_id int(11) unsigned default null
+, primary key(record_id)
+, unique index event_category_unq (event_category)
+) engine = innodb default charset = latin1;
+create index event_categories_tenant_record_id on event_categories(tenant_record_id);
+
+create table metrics (
+  record_id int(11) unsigned not null auto_increment
+, event_category_id integer not null
+, metric varchar(256) not null
+, tenant_record_id int(11) unsigned default null
+, primary key(record_id)
+, unique index metric_unq (event_category_id, metric)
+) engine = innodb default charset = latin1;
+create index metrics_tenant_record_id on metrics(tenant_record_id);
+
+create table timeline_chunks (
+  record_id bigint not null auto_increment
+, source_id integer not null
+, metric_id integer not null
+, sample_count integer not null
+, start_time integer not null
+, end_time integer not null
+, not_valid tinyint default 0
+, aggregation_level tinyint default 0
+, dont_aggregate tinyint default 0
+, in_row_samples varbinary(400) default null
+, blob_samples mediumblob default null
+, primary key(record_id)
+, unique index source_id_timeline_chunk_metric_idx (source_id, metric_id, start_time, aggregation_level)
+, index valid_agg_host_start_time (not_valid, aggregation_level, source_id, metric_id, start_time)
+) engine = innodb default charset = latin1;
+
+create table last_start_times (
+  time_inserted int not null primary key
+, start_times mediumtext not null
+) engine = innodb default charset = latin1;
+
+insert ignore into timeline_chunks(record_id, source_id, metric_id, sample_count, start_time, end_time, in_row_samples, blob_samples)
+                           values (0, 0, 0, 0, 0, 0, null, null);
+
+create table timeline_rolled_up_chunk (
+  record_id bigint not null auto_increment
+, source_id integer not null
+, metric_id integer not null
+, start_time date not null
+, end_time date not null
+, value bigint not null
+, account_record_id int(11) unsigned default null
+, tenant_record_id int(11) unsigned default null
+, primary key(record_id)
+) engine = innodb default charset = latin1;
+create index timeline_rolled_up_chunk_tenant_account_record_id on timeline_rolled_up_chunk(tenant_record_id, account_record_id);
diff --git a/meter/src/test/java/com/ning/billing/meter/MeterTestSuite.java b/meter/src/test/java/com/ning/billing/meter/MeterTestSuite.java
new file mode 100644
index 0000000..d6ae3a6
--- /dev/null
+++ b/meter/src/test/java/com/ning/billing/meter/MeterTestSuite.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you 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.ning.billing.meter;
+
+import com.ning.billing.KillbillTestSuite;
+
+public class MeterTestSuite extends KillbillTestSuite {
+
+}
diff --git a/meter/src/test/java/com/ning/billing/meter/MeterTestSuiteWithEmbeddedDB.java b/meter/src/test/java/com/ning/billing/meter/MeterTestSuiteWithEmbeddedDB.java
new file mode 100644
index 0000000..ec039b5
--- /dev/null
+++ b/meter/src/test/java/com/ning/billing/meter/MeterTestSuiteWithEmbeddedDB.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you 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.ning.billing.meter;
+
+import com.ning.billing.KillbillTestSuiteWithEmbeddedDB;
+
+public class MeterTestSuiteWithEmbeddedDB extends KillbillTestSuiteWithEmbeddedDB {
+
+}

pom.xml 6(+6 -0)

diff --git a/pom.xml b/pom.xml
index 1d86c33..30b5439 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,6 +46,7 @@
         <module>entitlement</module>
         <module>invoice</module>
         <module>junction</module>
+        <module>meter</module>
         <module>overdue</module>
         <module>payment</module>
         <module>usage</module>
@@ -194,6 +195,11 @@
             </dependency>
             <dependency>
                 <groupId>com.ning.billing</groupId>
+                <artifactId>killbill-meter</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.ning.billing</groupId>
                 <artifactId>killbill-overdue</artifactId>
                 <version>${project.version}</version>
             </dependency>

server/pom.xml 8(+8 -0)

diff --git a/server/pom.xml b/server/pom.xml
index 1cc4442..c184f05 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -197,6 +197,10 @@
         </dependency>
         <dependency>
             <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-meter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
             <artifactId>killbill-overdue</artifactId>
         </dependency>
         <dependency>
@@ -204,6 +208,10 @@
             <artifactId>killbill-tenant</artifactId>
         </dependency>
         <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-usage</artifactId>
+        </dependency>
+        <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
             <version>11.0.2</version>

usage/pom.xml 16(+0 -16)

diff --git a/usage/pom.xml b/usage/pom.xml
index 6b03f41..f607f7b 100644
--- a/usage/pom.xml
+++ b/usage/pom.xml
@@ -25,22 +25,6 @@
             <artifactId>jdbi</artifactId>
         </dependency>
         <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-core</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.fasterxml.jackson.datatype</groupId>
-            <artifactId>jackson-datatype-joda</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.fasterxml.jackson.dataformat</groupId>
-            <artifactId>jackson-dataformat-smile</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.fasterxml.util</groupId>
-            <artifactId>low-gc-membuffers</artifactId>
-        </dependency>
-        <dependency>
             <groupId>com.google.inject</groupId>
             <artifactId>guice</artifactId>
             <scope>provided</scope>

usage/README.md 5(+5 -0)

diff --git a/usage/README.md b/usage/README.md
new file mode 100644
index 0000000..78fef20
--- /dev/null
+++ b/usage/README.md
@@ -0,0 +1,5 @@
+Usage
+-----
+
+The usage module handles billing of units consumed, based on rolled-up usage information (e.g. per day or per month).
+See the meter module to handle aggregation of units.
diff --git a/usage/src/main/java/com/ning/billing/usage/api/user/DefaultRolledUpUsage.java b/usage/src/main/java/com/ning/billing/usage/api/user/DefaultRolledUpUsage.java
new file mode 100644
index 0000000..83ddeff
--- /dev/null
+++ b/usage/src/main/java/com/ning/billing/usage/api/user/DefaultRolledUpUsage.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you 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.ning.billing.usage.api.user;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.usage.api.RolledUpUsage;
+import com.ning.billing.usage.dao.RolledUpUsageModelDao;
+
+public class DefaultRolledUpUsage implements RolledUpUsage {
+
+    private final UUID subscriptionId;
+    private final String unitType;
+    private final DateTime startTime;
+    private final DateTime endTime;
+    private final BigDecimal amount;
+
+    public DefaultRolledUpUsage(final UUID subscriptionId, final String unitType, final DateTime startTime, final DateTime endTime,
+                                final BigDecimal amount) {
+        this.subscriptionId = subscriptionId;
+        this.unitType = unitType;
+        this.startTime = startTime;
+        this.endTime = endTime;
+        this.amount = amount;
+    }
+
+    public DefaultRolledUpUsage(final RolledUpUsageModelDao usageForSubscription) {
+        this(usageForSubscription.getSubscriptionId(), usageForSubscription.getUnitType(), usageForSubscription.getStartTime(),
+             usageForSubscription.getEndTime(), usageForSubscription.getAmount());
+    }
+
+    @Override
+    public UUID getSubscriptionId() {
+        return subscriptionId;
+    }
+
+    public String getUnitType() {
+        return unitType;
+    }
+
+    @Override
+    public DateTime getStartTime() {
+        return startTime;
+    }
+
+    @Override
+    public DateTime getEndTime() {
+        return endTime;
+    }
+
+    @Override
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("DefaultRolledUpUsage");
+        sb.append("{subscriptionId=").append(subscriptionId);
+        sb.append(", unitType='").append(unitType).append('\'');
+        sb.append(", startTime=").append(startTime);
+        sb.append(", endTime=").append(endTime);
+        sb.append(", amount=").append(amount);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final DefaultRolledUpUsage that = (DefaultRolledUpUsage) o;
+
+        if (amount != null ? !amount.equals(that.amount) : that.amount != null) {
+            return false;
+        }
+        if (endTime != null ? !endTime.equals(that.endTime) : that.endTime != null) {
+            return false;
+        }
+        if (unitType != null ? !unitType.equals(that.unitType) : that.unitType != null) {
+            return false;
+        }
+        if (startTime != null ? !startTime.equals(that.startTime) : that.startTime != null) {
+            return false;
+        }
+        if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = subscriptionId != null ? subscriptionId.hashCode() : 0;
+        result = 31 * result + (unitType != null ? unitType.hashCode() : 0);
+        result = 31 * result + (startTime != null ? startTime.hashCode() : 0);
+        result = 31 * result + (endTime != null ? endTime.hashCode() : 0);
+        result = 31 * result + (amount != null ? amount.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/usage/src/main/java/com/ning/billing/usage/api/user/DefaultUsageUserApi.java b/usage/src/main/java/com/ning/billing/usage/api/user/DefaultUsageUserApi.java
index 4df52f2..aa65345 100644
--- a/usage/src/main/java/com/ning/billing/usage/api/user/DefaultUsageUserApi.java
+++ b/usage/src/main/java/com/ning/billing/usage/api/user/DefaultUsageUserApi.java
@@ -16,76 +16,45 @@
 
 package com.ning.billing.usage.api.user;
 
+import java.math.BigDecimal;
 import java.util.UUID;
 
 import javax.inject.Inject;
 
 import org.joda.time.DateTime;
 
-import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
-import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.ObjectType;
+import com.ning.billing.usage.api.RolledUpUsage;
 import com.ning.billing.usage.api.UsageUserApi;
 import com.ning.billing.usage.dao.RolledUpUsageDao;
-import com.ning.billing.usage.timeline.TimelineEventHandler;
+import com.ning.billing.usage.dao.RolledUpUsageModelDao;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
-import com.ning.billing.util.clock.Clock;
-import com.ning.billing.util.svcapi.entitlement.EntitlementInternalApi;
-
-import com.google.common.collect.ImmutableMap;
+import com.ning.billing.util.callcontext.TenantContext;
 
 public class DefaultUsageUserApi implements UsageUserApi {
 
-    private static final String DEFAULT_EVENT_TYPE = "__DefaultUsageUserApi__";
-
     private final RolledUpUsageDao rolledUpUsageDao;
-    private final TimelineEventHandler timelineEventHandler;
-    private final EntitlementInternalApi entitlementApi;
-    private final Clock clock;
     private final InternalCallContextFactory internalCallContextFactory;
 
     @Inject
     public DefaultUsageUserApi(final RolledUpUsageDao rolledUpUsageDao,
-                               final TimelineEventHandler timelineEventHandler,
-                               final EntitlementInternalApi entitlementApi,
-                               final Clock clock,
                                final InternalCallContextFactory internalCallContextFactory) {
         this.rolledUpUsageDao = rolledUpUsageDao;
-        this.timelineEventHandler = timelineEventHandler;
-        this.entitlementApi = entitlementApi;
-        this.clock = clock;
         this.internalCallContextFactory = internalCallContextFactory;
     }
 
     @Override
-    public void incrementUsage(final UUID bundleId, final String metricName, final CallContext context) throws EntitlementUserApiException {
-        recordUsage(bundleId, metricName, clock.getUTCNow(), 1, context);
-    }
-
-    @Override
-    public void recordUsage(final UUID bundleId, final String metricName, final DateTime timestamp, final long value, final CallContext context) throws EntitlementUserApiException {
-        final String sourceName = getSourceNameFromBundleId(bundleId);
-        timelineEventHandler.record(sourceName, DEFAULT_EVENT_TYPE, timestamp, ImmutableMap.<String, Object>of(metricName, value), createInternalCallContext(bundleId, context));
+    public void recordRolledUpUsage(final UUID subscriptionId, final String unitType, final DateTime startTime, final DateTime endTime,
+                                    final BigDecimal amount, final CallContext context) {
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(subscriptionId, ObjectType.SUBSCRIPTION, context);
+        rolledUpUsageDao.record(subscriptionId, unitType, startTime, endTime, amount, internalCallContext);
     }
 
     @Override
-    public void recordRolledUpUsage(final UUID bundleId, final String metricName, final DateTime startDate, final DateTime endDate,
-                                    final long value, final CallContext context) throws EntitlementUserApiException {
-        final String sourceName = getSourceNameFromBundleId(bundleId);
-
-        rolledUpUsageDao.record(sourceName, DEFAULT_EVENT_TYPE, metricName, startDate, endDate, value, createInternalCallContext(bundleId, context));
-    }
-
-    private InternalCallContext createInternalCallContext(final UUID bundleId, final CallContext context) throws EntitlementUserApiException {
-        // Retrieve the bundle to get the account id for the internal call context
-        // API_FIX
-        final SubscriptionBundle bundle = null; // entitlementApi.getBundleFromId(bundleId, context);
-        return internalCallContextFactory.createInternalCallContext(bundle.getAccountId(), context);
-    }
-
-    private String getSourceNameFromBundleId(final UUID bundleId) {
-        // TODO we should do better
-        return bundleId.toString();
+    public RolledUpUsage getUsageForSubscription(final UUID subscriptionId, final TenantContext context) {
+        final RolledUpUsageModelDao usageForSubscription = rolledUpUsageDao.getUsageForSubscription(subscriptionId, internalCallContextFactory.createInternalTenantContext(context));
+        return new DefaultRolledUpUsage(usageForSubscription);
     }
 }
diff --git a/usage/src/main/java/com/ning/billing/usage/dao/DefaultRolledUpUsageDao.java b/usage/src/main/java/com/ning/billing/usage/dao/DefaultRolledUpUsageDao.java
index c4bd958..1bc4b78 100644
--- a/usage/src/main/java/com/ning/billing/usage/dao/DefaultRolledUpUsageDao.java
+++ b/usage/src/main/java/com/ning/billing/usage/dao/DefaultRolledUpUsageDao.java
@@ -16,14 +16,15 @@
 
 package com.ning.billing.usage.dao;
 
+import java.math.BigDecimal;
+import java.util.UUID;
+
 import javax.inject.Inject;
 
 import org.joda.time.DateTime;
-import org.skife.jdbi.v2.Transaction;
-import org.skife.jdbi.v2.TransactionStatus;
 
-import com.ning.billing.usage.timeline.persistent.TimelineSqlDao;
 import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.callcontext.InternalTenantContext;
 
 public class DefaultRolledUpUsageDao implements RolledUpUsageDao {
 
@@ -35,38 +36,16 @@ public class DefaultRolledUpUsageDao implements RolledUpUsageDao {
     }
 
     @Override
-    public void record(final String source, final String eventType, final String metricName, final DateTime startDate,
-                       final DateTime endDate, final long value, final InternalCallContext context) {
-        rolledUpUsageSqlDao.inTransaction(new Transaction<Void, RolledUpUsageSqlDao>() {
-            @Override
-            public Void inTransaction(final RolledUpUsageSqlDao transactional, final TransactionStatus status) throws Exception {
-                final TimelineSqlDao timelineSqlDao = transactional.become(TimelineSqlDao.class);
-
-                // Create the source if it doesn't exist
-                Integer sourceId = timelineSqlDao.getSourceId(source, context);
-                if (sourceId == null) {
-                    timelineSqlDao.addSource(source, context);
-                    sourceId = timelineSqlDao.getSourceId(source, context);
-                }
-
-                // Create the category if it doesn't exist
-                Integer categoryId = timelineSqlDao.getEventCategoryId(eventType, context);
-                if (categoryId == null) {
-                    timelineSqlDao.addEventCategory(eventType, context);
-                    categoryId = timelineSqlDao.getEventCategoryId(eventType, context);
-                }
-
-                // Create the metric if it doesn't exist
-                Integer metricId = timelineSqlDao.getMetricId(categoryId, metricName, context);
-                if (metricId == null) {
-                    timelineSqlDao.addMetric(categoryId, metricName, context);
-                    metricId = timelineSqlDao.getMetricId(categoryId, metricName, context);
-                }
-
-                transactional.record(sourceId, metricId, startDate.toDate(), endDate.toDate(), value, context);
+    public void record(final UUID subscriptionId, final String unitType, final DateTime startTime, final DateTime endTime,
+                       final BigDecimal amount, final InternalCallContext context) {
+        final RolledUpUsageModelDao rolledUpUsageModelDao = new RolledUpUsageModelDao(subscriptionId, unitType, startTime,
+                                                                                      endTime, amount
+        );
+        rolledUpUsageSqlDao.create(rolledUpUsageModelDao, context);
+    }
 
-                return null;
-            }
-        });
+    @Override
+    public RolledUpUsageModelDao getUsageForSubscription(final UUID subscriptionId, final InternalTenantContext context) {
+        return rolledUpUsageSqlDao.getUsageForSubscription(subscriptionId, context);
     }
 }
diff --git a/usage/src/main/java/com/ning/billing/usage/dao/RolledUpUsageDao.java b/usage/src/main/java/com/ning/billing/usage/dao/RolledUpUsageDao.java
index 25419d4..3907589 100644
--- a/usage/src/main/java/com/ning/billing/usage/dao/RolledUpUsageDao.java
+++ b/usage/src/main/java/com/ning/billing/usage/dao/RolledUpUsageDao.java
@@ -16,16 +16,18 @@
 
 package com.ning.billing.usage.dao;
 
+import java.math.BigDecimal;
+import java.util.UUID;
+
 import org.joda.time.DateTime;
 
 import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.callcontext.InternalTenantContext;
 
-/**
- * Dao to record already rolled-up usage data (rolled-up by the user).
- * For raw tracking of the data, @see TimelineEventHandler.
- */
 public interface RolledUpUsageDao {
 
-    public void record(final String sourceName, final String eventType, final String metricName, final DateTime startDate,
-                       final DateTime endDate, final long value, final InternalCallContext context);
+    void record(UUID subscriptionId, String unitType, DateTime startTime,
+                DateTime endTime, BigDecimal amount, InternalCallContext context);
+
+    RolledUpUsageModelDao getUsageForSubscription(UUID subscriptionId, InternalTenantContext context);
 }
diff --git a/usage/src/main/java/com/ning/billing/usage/dao/RolledUpUsageModelDao.java b/usage/src/main/java/com/ning/billing/usage/dao/RolledUpUsageModelDao.java
new file mode 100644
index 0000000..7b03aec
--- /dev/null
+++ b/usage/src/main/java/com/ning/billing/usage/dao/RolledUpUsageModelDao.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you 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.ning.billing.usage.dao;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+public class RolledUpUsageModelDao {
+
+    private UUID id;
+    private UUID subscriptionId;
+    private String unitType;
+    private DateTime startTime;
+    private DateTime endTime;
+    private BigDecimal amount;
+
+    public RolledUpUsageModelDao() { /* For the DAO mapper */ }
+
+    public RolledUpUsageModelDao(final UUID subscriptionId, final String unitType, final DateTime startTime,
+                                 final DateTime endTime, final BigDecimal amount) {
+        this.id = UUID.randomUUID();
+        this.subscriptionId = subscriptionId;
+        this.unitType = unitType;
+        this.startTime = startTime;
+        this.endTime = endTime;
+        this.amount = amount;
+    }
+
+    public UUID getId() {
+        return id;
+    }
+
+    public UUID getSubscriptionId() {
+        return subscriptionId;
+    }
+
+    public String getUnitType() {
+        return unitType;
+    }
+
+    public DateTime getStartTime() {
+        return startTime;
+    }
+
+    public DateTime getEndTime() {
+        return endTime;
+    }
+
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("RolledUpUsageModelDao");
+        sb.append("{id=").append(id);
+        sb.append(", subscriptionId=").append(subscriptionId);
+        sb.append(", unitType='").append(unitType).append('\'');
+        sb.append(", startTime=").append(startTime);
+        sb.append(", endTime=").append(endTime);
+        sb.append(", amount=").append(amount);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final RolledUpUsageModelDao that = (RolledUpUsageModelDao) o;
+
+        if (amount != null ? !amount.equals(that.amount) : that.amount != null) {
+            return false;
+        }
+        if (endTime != null ? !endTime.equals(that.endTime) : that.endTime != null) {
+            return false;
+        }
+        if (id != null ? !id.equals(that.id) : that.id != null) {
+            return false;
+        }
+        if (unitType != null ? !unitType.equals(that.unitType) : that.unitType != null) {
+            return false;
+        }
+        if (startTime != null ? !startTime.equals(that.startTime) : that.startTime != null) {
+            return false;
+        }
+        if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = id != null ? id.hashCode() : 0;
+        result = 31 * result + (subscriptionId != null ? subscriptionId.hashCode() : 0);
+        result = 31 * result + (unitType != null ? unitType.hashCode() : 0);
+        result = 31 * result + (startTime != null ? startTime.hashCode() : 0);
+        result = 31 * result + (endTime != null ? endTime.hashCode() : 0);
+        result = 31 * result + (amount != null ? amount.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/usage/src/main/java/com/ning/billing/usage/dao/RolledUpUsageSqlDao.java b/usage/src/main/java/com/ning/billing/usage/dao/RolledUpUsageSqlDao.java
index 52db57c..8a78f4a 100644
--- a/usage/src/main/java/com/ning/billing/usage/dao/RolledUpUsageSqlDao.java
+++ b/usage/src/main/java/com/ning/billing/usage/dao/RolledUpUsageSqlDao.java
@@ -16,25 +16,26 @@
 
 package com.ning.billing.usage.dao;
 
-import java.util.Date;
+import java.util.UUID;
 
 import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.BindBean;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.UseStringTemplate3StatementLocator;
 
 import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.callcontext.InternalTenantContextBinder;
 
-@ExternalizedSqlViaStringTemplate3()
-public interface RolledUpUsageSqlDao extends Transactional<RolledUpUsageSqlDao>, Transmogrifier {
+@UseStringTemplate3StatementLocator()
+public interface RolledUpUsageSqlDao {
 
     @SqlUpdate
-    public void record(@Bind("sourceId") final int sourceId,
-                       @Bind("metricId") final int metricId,
-                       @Bind("startTime") final Date startTime,
-                       @Bind("endTime") final Date endTime,
-                       @Bind("value") final long value,
+    public void create(@BindBean RolledUpUsageModelDao rolledUpUsage,
                        @InternalTenantContextBinder final InternalCallContext context);
+
+    @SqlQuery
+    public RolledUpUsageModelDao getUsageForSubscription(@Bind("subscriptionId") final UUID subscriptionId,
+                                                         @InternalTenantContextBinder final InternalTenantContext context);
 }
diff --git a/usage/src/main/java/com/ning/billing/usage/glue/UsageModule.java b/usage/src/main/java/com/ning/billing/usage/glue/UsageModule.java
index 1eafd1e..45fd6a2 100644
--- a/usage/src/main/java/com/ning/billing/usage/glue/UsageModule.java
+++ b/usage/src/main/java/com/ning/billing/usage/glue/UsageModule.java
@@ -16,73 +16,11 @@
 
 package com.ning.billing.usage.glue;
 
-import java.io.IOException;
-
-import org.skife.config.ConfigSource;
-import org.skife.config.ConfigurationObjectFactory;
-import org.skife.config.SimplePropertyConfigSource;
-
-import com.ning.billing.util.config.UsageConfig;
-import com.ning.billing.usage.timeline.codec.DefaultSampleCoder;
-import com.ning.billing.usage.timeline.codec.SampleCoder;
-import com.ning.billing.usage.timeline.persistent.FileBackedBuffer;
-import com.ning.billing.usage.timeline.persistent.TimelineDao;
-import com.ning.billing.usage.timeline.times.DefaultTimelineCoder;
-import com.ning.billing.usage.timeline.times.TimelineCoder;
-
 import com.google.inject.AbstractModule;
 
 public class UsageModule extends AbstractModule {
 
-    private final ConfigSource configSource;
-
-    public UsageModule() {
-        this(new SimplePropertyConfigSource(System.getProperties()));
-    }
-
-    public UsageModule(final ConfigSource configSource) {
-        this.configSource = configSource;
-    }
-
-    protected UsageConfig installConfig() {
-        final UsageConfig config = new ConfigurationObjectFactory(configSource).build(UsageConfig.class);
-        bind(UsageConfig.class).toInstance(config);
-
-        return config;
-    }
-
-    protected void configureFileBackedBuffer(final UsageConfig config) {
-        // Persistent buffer for in-memory samples
-        try {
-            final boolean deleteFilesOnClose = config.getShutdownSaveMode().equals("save_all_timelines");
-            final FileBackedBuffer fileBackedBuffer = new FileBackedBuffer(config.getSpoolDir(), "TimelineEventHandler", deleteFilesOnClose, config.getSegmentsSize(), config.getMaxNbSegments());
-            bind(FileBackedBuffer.class).toInstance(fileBackedBuffer);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    protected void configureDao() {
-        bind(TimelineDao.class).toProvider(CachingDefaultTimelineDaoProvider.class).asEagerSingleton();
-    }
-
-    protected void configureTimelineObjects() {
-        bind(TimelineCoder.class).to(DefaultTimelineCoder.class).asEagerSingleton();
-        bind(SampleCoder.class).to(DefaultSampleCoder.class).asEagerSingleton();
-    }
-
     @Override
     protected void configure() {
-        final UsageConfig config = installConfig();
-
-        configureFileBackedBuffer(config);
-        configureDao();
-        configureTimelineObjects();
-
-        // TODO
-        //configureTimelineAggregator();
-        //configureBackgroundDBChunkWriter();
-        //configureReplayer();
     }
 }
-
diff --git a/usage/src/main/resources/com/ning/billing/usage/dao/RolledUpUsageSqlDao.sql.stg b/usage/src/main/resources/com/ning/billing/usage/dao/RolledUpUsageSqlDao.sql.stg
new file mode 100644
index 0000000..e028757
--- /dev/null
+++ b/usage/src/main/resources/com/ning/billing/usage/dao/RolledUpUsageSqlDao.sql.stg
@@ -0,0 +1,51 @@
+group RolledUpUsageSqlDao;
+
+tableName() ::= "usage"
+
+tableFields(prefix) ::= <<
+  <prefix>id
+, <prefix>subscription_id
+, <prefix>unit_type
+, <prefix>start_time
+, <prefix>end_time
+, <prefix>amount
+, <prefix>created_by
+, <prefix>created_date
+, <prefix>account_record_id
+, <prefix>tenant_record_id
+>>
+
+tableValues() ::= <<
+  :id
+, :subscriptionId
+, :unitType
+, :startTime
+, :endTime
+, :amount
+, :userName
+, :createdDate
+, :accountRecordId
+, :tenantRecordId
+>>
+
+CHECK_TENANT(prefix) ::= "<prefix>tenant_record_id = :tenantRecordId"
+AND_CHECK_TENANT(prefix) ::= "and <CHECK_TENANT(prefix)>"
+
+create() ::= <<
+insert into <tableName()> (
+  <tableFields()>
+)
+values (
+  <tableValues()>
+)
+;
+>>
+
+getUsageForSubscription() ::= <<
+select
+  <tableFields("t.")>
+from <tableName()> t
+where subscription_id = :subscriptionId
+<AND_CHECK_TENANT()>
+;
+>>
diff --git a/usage/src/main/resources/com/ning/billing/usage/ddl.sql b/usage/src/main/resources/com/ning/billing/usage/ddl.sql
index 23f046d..c2cd87c 100644
--- a/usage/src/main/resources/com/ning/billing/usage/ddl.sql
+++ b/usage/src/main/resources/com/ning/billing/usage/ddl.sql
@@ -1,71 +1,18 @@
-create table sources (
-  record_id int(11) unsigned not null auto_increment
-, bundle_id char(36) default null
-, subscription_id char(36) default null
-, created_date datetime default null
-, created_by varchar(50) default null
-, updated_date datetime default null
-, updated_by varchar(50) default null
-, account_record_id int(11) unsigned default null
-, tenant_record_id int(11) unsigned default null
-, primary key(record_id)
-, index created_date_record_id_dx (created_date, record_id)
-) engine = innodb default charset = latin1;
-create index sources_tenant_account_record_id on sources(tenant_record_id, account_record_id);
-
-create table event_categories (
-  record_id integer not null auto_increment
-, event_category varchar(256) not null
-, tenant_record_id int(11) unsigned default null
-, primary key(record_id)
-, unique index event_category_unq (event_category)
-) engine = innodb default charset = latin1;
-create index event_categories_tenant_record_id on event_categories(tenant_record_id);
-
-create table metrics (
-  record_id int(11) unsigned not null auto_increment
-, event_category_id integer not null
-, metric varchar(256) not null
-, tenant_record_id int(11) unsigned default null
-, primary key(record_id)
-, unique index metric_unq (event_category_id, metric)
-) engine = innodb default charset = latin1;
-create index metrics_tenant_record_id on metrics(tenant_record_id);
-
-create table timeline_chunks (
-  record_id bigint not null auto_increment
-, source_id integer not null
-, metric_id integer not null
-, sample_count integer not null
-, start_time integer not null
-, end_time integer not null
-, not_valid tinyint default 0
-, aggregation_level tinyint default 0
-, dont_aggregate tinyint default 0
-, in_row_samples varbinary(400) default null
-, blob_samples mediumblob default null
-, primary key(record_id)
-, unique index source_id_timeline_chunk_metric_idx (source_id, metric_id, start_time, aggregation_level)
-, index valid_agg_host_start_time (not_valid, aggregation_level, source_id, metric_id, start_time)
-) engine = innodb default charset = latin1;
-
-create table last_start_times (
-  time_inserted int not null primary key
-, start_times mediumtext not null
-) engine = innodb default charset = latin1;
-
-insert ignore into timeline_chunks(record_id, source_id, metric_id, sample_count, start_time, end_time, in_row_samples, blob_samples)
-                           values (0, 0, 0, 0, 0, 0, null, null);
-
-create table timeline_rolled_up_chunk (
-  record_id bigint not null auto_increment
-, source_id integer not null
-, metric_id integer not null
-, start_time date not null
-, end_time date not null
-, value bigint not null
-, account_record_id int(11) unsigned default null
-, tenant_record_id int(11) unsigned default null
-, primary key(record_id)
-) engine = innodb default charset = latin1;
-create index timeline_rolled_up_chunk_tenant_account_record_id on timeline_rolled_up_chunk(tenant_record_id, account_record_id);
+DROP TABLE IF EXISTS rolled_up_usage;
+CREATE TABLE rolled_up_usage (
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    id char(36) NOT NULL,
+    subscription_id char(36),
+    unit_type varchar(50),
+    start_date date NOT NULL,
+    end_date date,
+    amount numeric(10,10) NOT NULL,
+    created_by varchar(50) NOT NULL,
+    created_date datetime NOT NULL,
+    account_record_id int(11) unsigned default null,
+    tenant_record_id int(11) unsigned default null,
+    PRIMARY KEY(record_id)
+) ENGINE=innodb;
+CREATE UNIQUE INDEX rolled_up_usage_id ON rolled_up_usage(id);
+CREATE INDEX rolled_up_usage_subscription_id ON rolled_up_usage(subscription_id ASC);
+CREATE INDEX rolled_up_usage_tenant_account_record_id ON rolled_up_usage(tenant_record_id, account_record_id);