SlideShare a Scribd company logo
MongoDB World 2014
by S.D.O.C. Ltd.
Billing on top of MongoDB
Ofer Cohen
Who Am I ?
● Open source evangelist
● Former Board member of OpenSourceMatters
(Non-profit organization behind Joomla)
● S.D.O.C. Ltd. Co-Founder
FUD
● FUD: Fear, Uncertainty and Doubt
● Tactic used in sales, marketing, public
relations, politics and propaganda.
Wikipedia
FUD
Preferred name for the presentation
S.D.O.C. Ltd. vision
We increase business success through great
open source technologies and services
○ Open & Transparent
○ Don't reinvent the wheel
○ High Quality
○ Lean & Effective
How did we get to billing (history)
● Client: Golan Telecom (Israel)
How did we get to billing (history)
● Client: Golan Telecom
○ New & lean player in the market
○ 0=>~1M subscribers in few years
○ Limited resources & loves open source
How did we get to billing (history)
● Client: Golan Telecom
○ Start up environment from Day 1
○ Short and aggressive time to market
○ Unlimited plan for ~25$
○ Customer can do almost everything using the website
■ Obviously requires 24/7 uptime
How did we get to billing (history)
● Start with Anti-Fraud solution
● 2 Different data structure, 2 separated tables
○ Outgoing calls (MOC)
○ incoming calls (MTC)
○ SMS (duration=0 means SMS)
Anti-Fraud in RDBMS
How did it look like? Example code...
$base_query = "SELECT imsi FROM moc WHERE callEventStartTimeStamp >=" . $start
. " UNION SELECT imsi FROM mtc WHERE callEventStartTimeStamp >=" . $start
$base_query = "SELECT imsi FROM (" . $base_query . ") AS qry ";
if (isset($args['imsi']))
$base_query .= "WHERE imsi = '" . $this->_connection->real_escape_string($args['imsi']) . "'";
$base_query .= "GROUP BY imsi ";
$mtc_join_query = "SELECT 'mtc' AS type, imsi, SUM(callEventDuration) AS duration, SUM(CEILING(callEventDuration/60)*60) AS duration_round "
. ", SUM(chargeAmount) charge "
. ", SUM(IF(SUBSTRING(callingNumber, 1, 3)='972', callEventDuration, IF(CHAR_LENGTH(callingNumber)<=10, callEventDuration, 0))) AS israel_duration
"
. ", SUM(IF(SUBSTRING(callingNumber, 1, 3)='972', CEILING(callEventDuration/60)*60, IF(CHAR_LENGTH(callingNumber)<=10, CEILING
(callEventDuration/60)*60, 0))) AS israel_duration_round "
. ", SUM(IF(SUBSTRING(callingNumber, 1, 3)!='972', IF(CHAR_LENGTH(callingNumber)>10, callEventDuration, 0), 0)) AS non_israel_duration "
. ", SUM(IF(SUBSTRING(callingNumber, 1, 3)!='972', IF(CHAR_LENGTH(callingNumber)>10, CEILING(callEventDuration/60)*60, 0), 0)) AS
non_israel_duration_round "
. ", SUM(IF(callEventDuration = 0, 1, 0)) AS sms_count "
Anti-Fraud in RDBMS
How did it look like? Example code...
. "FROM mtc "
. "WHERE callEventStartTimeStamp >=" . $start . " "
. "GROUP BY type, imsi";
$moc_join_query = "SELECT 'moc' AS type, imsi, SUM(callEventDuration) AS duration, SUM(CEILING(callEventDuration/60)*60) AS duration_round "
. ", SUM(chargeAmount) charge "
. ", SUM(IF(SUBSTRING(connectedNumber, 1, 3)='972', callEventDuration, IF(CHAR_LENGTH(connectedNumber)<=10, callEventDuration, 0))) AS
israel_duration "
. ", SUM(IF(SUBSTRING(connectedNumber, 1, 3)='972', CEILING(callEventDuration/60)*60, IF(CHAR_LENGTH(connectedNumber)<=10, CEILING
(callEventDuration/60)*60, 0))) AS israel_duration_round "
. ", SUM(IF(SUBSTRING(connectedNumber, 1, 3)!='972', IF(CHAR_LENGTH(connectedNumber)>10, callEventDuration, 0), 0)) AS non_israel_duration "
. ", SUM(IF(SUBSTRING(connectedNumber, 1, 3)!='972', IF(CHAR_LENGTH(connectedNumber)>10, CEILING(callEventDuration/60)*60, 0), 0)) AS
non_israel_duration_round "
. ", SUM(IF(callEventDuration = 0, 1, 0)) AS sms_count "
. "FROM moc "
. "WHERE callEventStartTimeStamp >=" . $start . " "
. "GROUP BY type, imsi";
Anti-Fraud in RDBMS
How did it look like? Example code...
$group_query = "SELECT base.imsi, moc.duration AS moc_duration, moc.charge AS moc_charge, "
. "mtc.duration AS mtc_duration, mtc.charge AS mtc_charge, "
. "mtc.duration_round AS mtc_duration_round, moc.duration_round AS moc_duration_round, "
. "moc.israel_duration AS moc_israel_duration, moc.non_israel_duration AS moc_non_israel_duration, "
. "moc.israel_duration_round AS moc_israel_duration_round, moc.non_israel_duration_round AS
moc_non_israel_duration_round, "
. "mtc.israel_duration AS mtc_israel_duration, mtc.non_israel_duration AS mtc_non_israel_duration, "
. "mtc.israel_duration_round AS mtc_israel_duration_round, mtc.non_israel_duration_round AS
mtc_non_israel_duration_round, "
. "mtc.sms_count AS mtc_sms_count, moc.sms_count AS moc_sms_count "
. "FROM "
. "( " . $base_query . " ) AS base "
. " LEFT JOIN (" . $mtc_join_query . " ) AS mtc ON base.imsi = mtc.imsi "
. " LEFT JOIN (" . $moc_join_query . " ) AS moc ON base.imsi = moc.imsi " ;
Anti-Fraud in RDBMS
How did it feel…?
After moving to Mongo
● One main collection for 2 types (MOC & MTC)
● Aggregation framework is much more simple
After moving to Mongo
$base_match = array(
'$match' => array(
'source' => 'nrtrde',
'unified_record_time' => array('$gte' => new MongoDate($charge_time)),
)
);
$where = array(
'$match' => array(
'record_type' => 'MOC',
'connectedNumber' => array('$regex' => '^972'),
'event_stamp' => array('$exists' => false),
'deposit_stamp' => array('$exists' => false),
'callEventDurationRound' => array('$gt' => 0), // not sms
),
);
$group = array(
'$group' => array(
"_id" => '$imsi',
"moc_israel" => array('$sum' => '$callEventDurationRound'),
'lines_stamps' => array('$addToSet' => '$stamp'),
),
);
$project = array(
'$project' => array(
'imsi' => '$_id',
'_id' => 0,
'moc_israel' => 1,
'lines_stamps' => 1,
),
);
After moving to Mongo
$having = array(
'$match' => array(
'moc_israel' => array('$gte' => Billrun_Factory::config()->getConfigValue('nrtrde.thresholds.moc.israel'))
),
);
$moc_israel = $lines->aggregate($base_match, $where, $group, $project, $having);
//sms out to all numbers
$where['$match']['record_type'] = 'MOC';
$where['$match']['callEventDurationRound'] = 0;
$group['$group']['sms_out'] = $group['$group']['mtc_all'];
unset($group['$group']['mtc_all']);
unset($having['$match']['mtc_all']);
$group['$group']['sms_out'] = array('$sum' => 1);
$having['$match']['sms_out'] = array('$gte' => Billrun_Factory::config()->getConfigValue('nrtrde.thresholds.smsout'));
$project['$project']['sms_out'] = 1;
unset($project['$project']['mtc_all']);
$sms_out = $lines->aggregate($base_match, $where, $group, $project, $having);
What is billing?
● Group of processes of communications
service providers
● responsible to collect consumption data
● calculate charging and billing information
● produce bills to customers
● process their payments and manage debt
collection
Wikipedia
What is billing?
● This is how we see it
● The KISS way
Telecom today - database challenges
● Telecom situation world wide today:
○ Unlimited packages, extend 3g usage, with high
competition
○ High-volume - 4g, LTE
○ Different Events - different data structure
5 *main* data-structures (CDR) from different sources
● NSN - Calls
● SMSC - SMS
● MMSC - MMS
● GGSN - Data
● TAP3 - International usage
Billing and MongoDB - Pros
NSN Record
> db.lines.findOne({"usaget" : "call", aid:XXXXXXX})
{
"_id" : ObjectId("52bafd818f7ac3943a8b96dc"),
"stamp" : "87cea5dec484c8f6a19e44e77a2e15b4",
"record_type" : "01",
"record_number" : "13857000",
"record_status" : 0,
"exchange_id" : "000006270000",
"call_reference" : "700227d9a3",
"urt" : ISODate("2013-12-25T15:09:15Z"),
"charging_start_time" : "20131225170915",
"charging_end_time" : "20131225171517",
"call_reference_time" : "20131225170914",
"duration" : 362,
"calling_number" : "972546918666",
"called_number" : "26366667",
"call_type" : "3",
"chrg_type" : "0",
"called_imsi" : "000030000000000",
"imsi" : "000089000101076",
"in_circuit_group_name" : "",
"in_circuit_group" : "",
"out_circuit_group_name" : "NBZQZA8",
"out_circuit_group" : "0503",
"tariff_class" : "000000",
"called_number_ton" : "6",
"org_dur" : 362,
"type" : "nsn",
"source" : "binary",
"file" : "CF0322.DAT",
"log_stamp" : "0a3ffdb7c9ccc175e38be2fcc00f8c28",
"process_time" : "2013-12-25 17:35:22",
"usaget" : "call",
"usagev" : 362,
"arate" : DBRef("rates", ObjectId("521e07fcd88db0e73f0001c9")),
"aid" : XXXXXXX,
"sid" : YYYYY,
"plan" : "LARGE",
"aprice" : 0,
}
SMS Record
> db.lines.findOne({"usaget" : "sms", aid:XXXXXXX})
{
"_id" : ObjectId("52e52ff1d88db071648b4ad7"),
"stamp" : "9328f3aaa114aaba910910053a11b3e8",
"record_type" : "1",
"calling_number" : "000972546918666",
"calling_imsi" : "000089200000000",
"calling_msc" : "000972000000000",
"billable" : "000000000000000",
"called_number" : "000972547655380",
"called_imsi" : "425089109386379",
"called_msc" : "000972586279101",
"message_submition_time" : "140125192517",
"time_offest" : "02",
"message_delivery_time" : "140125192519",
"time_offest1" : "02",
"cause_of_terminition" : "100",
"call_reference" : "5137864939035049",
"message_length" : "050",
"concatenated" : "1",
"concatenated_from" : "09",
"source" : "separator_field_lines",
"type" : "smsc",
"log_stamp" : "a5950686e364d1400c13dd1857c3340e",
"file" : "140125192403_5735golan.cdr",
"process_time" : "2014-01-26 17:53:21",
"urt" : ISODate("2014-01-25T17:25:17Z"),
"usaget" : "sms",
"usagev" : 1,
"arate" : DBRef("rates", ObjectId("521e07fcd88db0e73f0001db")),
"aid" : XXXXXXX,
"sid" : YYYYY,
"plan" : "LARGE",
"aprice" : 0,
"usagesb" : 4,
"billrun" : "201402"
}
Data Record
> db.lines.findOne({"usaget" : "data", aid:XXXXXXX})
{
"_id" : ObjectId("539076678f7ac34a1d8b9367"),
"stamp" : "8a9f891ec85c5294c974a34653356055",
"imsi" : "400009209100000",
"served_imsi" : "400009209100000",
"ggsn_address" : "XX.XX.144.18",
"charging_id" : "2814645234",
"sgsn_address" : "XX.XX.145.9",
"served_pdp_address" : "XX.XX.237.95",
"urt" : ISODate("2014-06-05T09:34:47Z"),
"record_opening_time" : "20140605123447",
"ms_timezone" : "+03:00",
"node_id" : "GLTNGPT",
"served_msisdn" : "00002546918666",
"fbc_uplink_volume" : 61298,
"fbc_downlink_volume" : 217304,
"rating_group" : 0,
"type" : "ggsn",
"source" : "binary",
"file" : "GLTNGPT_-_0000056580.20140605_-_1251+0300",
"log_stamp" : "45a227ced1098bc76a44774eae04eb67",
"process_time" : "2014-06-05 16:39:40",
"usaget" : "data",
"usagev" : 278602,
"arate" : DBRef("rates", ObjectId("521e07fcd88db0e73f000200")),
"apr" : 0.020264916503503202,
"aid" : XXXXXXX,
"sid" : YYYYY,
"plan" : "LARGE",
"aprice" : 0,
"usagesb" : 478682116,
"billrun" : "201406"
}
TAP3 Record (intl roaming)
> db.lines.findOne({type:"tap3", aid:9073496})
{
"_id" : ObjectId("538d9ac98f7ac3e17d8b4fd6"),
"stamp" : "8f6cdc8662307ee2ed951ce640a585b5",
"basicCallInformation" : {
"GprsChargeableSubscriber" : {
"chargeableSubscriber" : {
"simChargeableSubscriber" : {
"imsi" : "400009209100000"
}
},
"pdpAddress" : "XX.XX.227.158"
},
"GprsDestination" : {
"AccessPointNameNI" : "internet.golantelecom.net.il"
},
"CallEventStartTimeStamp" : {
"localTimeStamp" : "20140529205131",
"TimeOffsetCode" : 0
},
"TotalCallEventDuration" : 163
},
"LocationInformation" : {
"gprsNetworkLocation" : {
"RecEntityCodeList" : {
"RecEntityCode" : [
"",
"u0000"
]
},
"LocationArea" : 00001,
"CellId" : 0001
},
"GeographicalLocation" : {
"ServingNetwork" : "MMMM"
}
},
"ImeiOrEsn" : false,
"GprsServiceUsed" : {
"DataVolumeIncoming" : 195120,
"DataVolumeOutgoing" : 48600,
"ChargeInformationList" : {
"ChargeInformation" : {
"ChargedItem" : "X",
"ExchangeRateCode" : 0,
"ChargeDetailList" : {
"ChargeDetail" : {
"ChargeType" : "00",
"Charge" : 001,
"ChargeableUnits" : 100000,
"ChargedUnits" : 100000,
"ChargeDetailTimeStamp" : {
"localTimeStamp" : "20140529205131",
"TimeOffset" : 0
}
}
}
}
}
},
"OperatorSpecInfoList" : {
"OperatorSpecInformation" : [
"00000000.0000",
"00000000.0000",
"00000000.0000"
]
},
"record_type" : "e",
"urt" : ISODate("2014-05-29T18:51:31Z"),
"tzoffset" : "+0200",
"imsi" : "400009209100000",
"serving_network" : "DEUE2",
"sdr" : 0.0001,
"exchange_rate" : 1.12078,
"type" : "tap3",
"file" : "CDBELHBISRGT02253",
"log_stamp" : "a0ad109c6e795f6c1feeef9ef649d937",
"process_time" : "2014-06-03 12:50:08",
"usaget" : "data",
"usagev" : 243720,
"arate" : DBRef("rates", ObjectId
("521e07fed88db0e73f000219")),
"apr" : 0.46640616,
"aid" : 9073496,
"sid" : 78288,
"plan" : "LARGE",
"out_plan" : 243720,
"aprice" : 0.46640616,
"usagesb" : 39139746,
"billrun" : "201406"
}
Billing and MongoDB - Pros
Loose coupling compared to traditional billing components
Old
w/o MongoDb New
with MongoDb
Billing and MongoDB - Pros
● One call can be spread on few CDRs
● BillRun merges records only on presentation
layer or export
● No DB-level aggregation nor accumulation
○ No need for mediation
● Use billing and monitoring CDR in one system
database
Billing and MongoDB - Pros
Sophisticated rating module
● Rating module depends on CDR structure
● Easy to implement recurring rating
Rate module example
MongoDB advantages with Billing
Invoice JSON2PDF process
● Use json as metadata for the PDF
● Easy to export
● Fast processing
MongoDB advantages with Billing
Invoice metadata
> db.billrun.findOne({billrun_key:"201405", aid:9073496})
{
"aid" : NumberLong(9073496),
"subs" : [
{
"sid" : NumberLong(78288),
"subscriber_status" : "open",
"current_plan" : DBRef("plans", ObjectId("51bd8dc9eb2f76d2178dd3dd")),
"next_plan" : DBRef("plans", ObjectId("51bd8dc9eb2f76d2178dd3dd")),
"kosher" : false,
"breakdown" : {
"in_plan" : {
"base" : {
"INTERNET_BILL_BY_VOLUME" : {
"totals" : {
"data" : {
"usagev" : NumberLong(1975547725),
"cost" : NumberLong(0),
"count" : NumberLong(1352)
}
},
"vat" : 0.18
},
"IL_MOBILE" : {
"totals" : {
"sms" : {
"usagev" : NumberLong(159),
"cost" : NumberLong(0),
"count" : NumberLong(159)
},
"call" : {
"usagev" : NumberLong(20788),
"cost" : NumberLong(0),
"count" : NumberLong(83)
}
},
"vat" : 0.18
},
"IL_FIX" : {
"totals" : {
"call" : {
"usagev" : NumberLong(1217),
"cost" : NumberLong(0),
"count" : NumberLong(16)
}
},
"vat" : 0.18
},
"INTERNAL_VOICE_MAIL_CALL" : {
"totals" : {
"call" : {
"usagev" : NumberLong(60),
"cost" : NumberLong(0),
"count" : NumberLong(2)
}
},
"vat" : 0.18
},
"service" : {
"cost" : 83.898305085,
"vat" : 0.18
}
},
"intl" : {
"KT_USA_NEW" : {
"totals" : {
"call" : {
"usagev" : NumberLong(149),
"cost" : NumberLong(0),
"count" : NumberLong(2)
}
},
"vat" : 0.18
}
}
},
MongoDB advantages with Billing
Invoice metadata
"credit" : {
"refund_vatable" : {
"CRM-REFUND_PROMOTION_1024-BILLRUN_201405" : -33.898305084746
}
}
},
"lines" : {
"data" : {
"counters" : {
"20140425" : {
"usagev" : NumberLong(11991615),
"aprice" : NumberLong(0),
"plan_flag" : "in"
},
…. /* data by date really long, so let’s cut it from this demonstration */
}
}
},
"totals" : {
"vatable" : 50.000000000254005,
"before_vat" : 50.000000000254005,
"after_vat" : 59.00000000029973
},
"costs" : {
"credit" : {
"refund" : {
"vatable" : -33.898305084746
}
},
"flat" : {
"vatable" : 83.898305085
}
}
},
{
"sid" : NumberLong(354961),
"subscriber_status" : "open",
"current_plan" : DBRef("plans", ObjectId("51bd8dc9eb2f76d2178dd3de")),
"next_plan" : DBRef("plans", ObjectId("51bd8dc9eb2f76d2178dd3de")),
"kosher" : false,
"breakdown" : {
"in_plan" : {
"base" : {
"service" : {
"cost" : 8.466101695,
"vat" : 0.18
}
}
}
},
"totals" : {
"vatable" : 8.466101695,
"before_vat" : 8.466101695,
"after_vat" : 9.9900000001
},
"costs" : {
"flat" : {
"vatable" : 8.466101695
}
}
}
],
"vat" : 0.18,
"billrun_key" : "201405",
"totals" : {
"before_vat" : 58.466101695254004,
"after_vat" : 68.99000000039973,
"vatable" : 58.466101695254004
},
"_id" : ObjectId("5382fd3cd88db0c31a8b74cc"),
"invoice_id" : NumberLong(14738102),
"invoice_file" : "201405_009073496_00014738102.xml"
}
Infrastructure
BETA
First Production
Today
Data center 1
Data center 2
Data center 1
Data center 2 Data center 1
Data center 2
Archive
App stack
BillRun application
BillRun core
PHP YAF
framework
Zend and more libraries
MongoDB
Web service Cli/Cron services
Mongodloid https://github.com/BillRun/Mongodloid
https://github.com/mongodb/mongo/
https://github.com/BillRun/system
https://github.com/zendframework/
https://github.com/twbs/bootstrap
https://github.com/laruence/php-yaf
Pay Attention! What to keep in mind
Transactional (tx) processes
● write concern - acknowledged (default in 2.4)
● findAndModify (A.K.A FAM) - document tx
● value=oldValue
● 2 phase commit - app side
● Transaction lock by design
Transaction workflow diagram
High performance tricks
● With SSD you get x20 more performance
● MongoDB loves RAM
● Follow MongoDB production notes
○ Readahead low as possible
○ THP - transparent hugepages
Pay Attention! What to keep in mind
More tips that happened to all
● Shorten property names
● MongoDB have database lock
○ Separate database for heavy-write collections
Pay Attention! What to keep in mind
TCO - MongoDB based solution
● 3 Months dev
● 3 Months QA
● Few days of maintenance infra and app
● Easy to add features
● Easy to scale
What can BillRun do
● 5,000 events per second
○ Including DB insert and transactional rating
● That means 157,680,000,000 events per year
● Create hundreds of millions invoices
Do not forget!
Thank you, Ofer Cohen
ofer@billrun.net
@oc666
Billing on Top of

More Related Content

PDF
CDR-Stats : VoIP Analytics Solution for Asterisk and FreeSWITCH with MongoDB
PDF
MongoDB .local Munich 2019: New Encryption Capabilities in MongoDB 4.2: A Dee...
PDF
MongoDB .local Chicago 2019: Using Client Side Encryption in MongoDB 4.2
PPTX
MongoDB San Francisco 2013: Hash-based Sharding in MongoDB 2.4 presented by B...
KEY
Esperwhispering
PDF
Statisztikai Spamszurok 2008
PDF
MongoDB Europe 2016 - Enabling the Internet of Things at Proximus - Belgium's...
PDF
MongoDB .local Houston 2019: Using Client Side Encryption in MongoDB 4.2
CDR-Stats : VoIP Analytics Solution for Asterisk and FreeSWITCH with MongoDB
MongoDB .local Munich 2019: New Encryption Capabilities in MongoDB 4.2: A Dee...
MongoDB .local Chicago 2019: Using Client Side Encryption in MongoDB 4.2
MongoDB San Francisco 2013: Hash-based Sharding in MongoDB 2.4 presented by B...
Esperwhispering
Statisztikai Spamszurok 2008
MongoDB Europe 2016 - Enabling the Internet of Things at Proximus - Belgium's...
MongoDB .local Houston 2019: Using Client Side Encryption in MongoDB 4.2

What's hot (20)

PDF
MongoDB .local Munich 2019: Aggregation Pipeline Power++: How MongoDB 4.2 Pip...
PDF
はじめてのMongoDB
PDF
MongoDB .local Munich 2019: Tips and Tricks++ for Querying and Indexing MongoDB
TXT
Books
PDF
MongoDB Performance Debugging
PDF
MongoDB Europe 2016 - Debugging MongoDB Performance
PDF
ChromeからMacBookのTouchIDでWebAuthenticationする ~Idance vol1~
 
KEY
Schema design
KEY
Mongo db presentation
PDF
20110514 mongo dbチューニング
PDF
GraphTalk Stockholm - Fraud Detection with Graphs
PDF
Asssignment2
PDF
Beyond Good & Evil: The nuts and bolts of DRM - Dave Cramer - ebookcraft 2017
PDF
Mongodb index 讀書心得
PDF
MongoDB World 2019: Using Client Side Encryption in MongoDB 4.2 Link
PDF
Mongodb debugging-performance-problems
PDF
MongoDB Oplog入門
PDF
NoSQL meets Microservices - Michael Hackstein
PDF
GraphTalk Helsinki - Fraud Analysis with Neo4j
PDF
Michael Hackstein - NoSQL meets Microservices - NoSQL matters Dublin 2015
MongoDB .local Munich 2019: Aggregation Pipeline Power++: How MongoDB 4.2 Pip...
はじめてのMongoDB
MongoDB .local Munich 2019: Tips and Tricks++ for Querying and Indexing MongoDB
Books
MongoDB Performance Debugging
MongoDB Europe 2016 - Debugging MongoDB Performance
ChromeからMacBookのTouchIDでWebAuthenticationする ~Idance vol1~
 
Schema design
Mongo db presentation
20110514 mongo dbチューニング
GraphTalk Stockholm - Fraud Detection with Graphs
Asssignment2
Beyond Good & Evil: The nuts and bolts of DRM - Dave Cramer - ebookcraft 2017
Mongodb index 讀書心得
MongoDB World 2019: Using Client Side Encryption in MongoDB 4.2 Link
Mongodb debugging-performance-problems
MongoDB Oplog入門
NoSQL meets Microservices - Michael Hackstein
GraphTalk Helsinki - Fraud Analysis with Neo4j
Michael Hackstein - NoSQL meets Microservices - NoSQL matters Dublin 2015
Ad

Viewers also liked (9)

PDF
Webinar slides: Replication Topology Changes for MySQL and MariaDB
PDF
Overview of Postgres 9.5
 
PPTX
What's new in MySQL Cluster 7.4 webinar charts
PDF
The Complete MariaDB Server Tutorial - Percona Live 2015
PDF
Performance improvements in PostgreSQL 9.5 and beyond
PDF
PostgreSQL Streaming Replication Cheatsheet
PPTX
MongoDB on EC2 and EBS
PDF
Deep Dive Into How To Monitor MySQL or MariaDB Galera Cluster / Percona XtraD...
ODP
JSON By Example
Webinar slides: Replication Topology Changes for MySQL and MariaDB
Overview of Postgres 9.5
 
What's new in MySQL Cluster 7.4 webinar charts
The Complete MariaDB Server Tutorial - Percona Live 2015
Performance improvements in PostgreSQL 9.5 and beyond
PostgreSQL Streaming Replication Cheatsheet
MongoDB on EC2 and EBS
Deep Dive Into How To Monitor MySQL or MariaDB Galera Cluster / Percona XtraD...
JSON By Example
Ad

Similar to MongoDB World 2014 - BillRun, Billing on top of MongoDB (20)

PDF
Mongo db world 2014 billrun
PPTX
MongoDB Evenings Minneapolis: MongoDB is Cool But When Should I Use It?
PDF
Single View of the Customer
PPTX
Webinar: How Financial Services Organizations Use MongoDB
PPT
Webinar: Making A Single View of the Customer Real with MongoDB
PPTX
Mongo db 2.4 time series data - Brignoli
PPTX
MongoDB in a Mainframe World
KEY
NOSQL101, Or: How I Learned To Stop Worrying And Love The Mongo!
PDF
From SQL to MongoDB
PPTX
Introduction to MongoDB at IGDTUW
PPTX
How Insurance Companies Use MongoDB
PPTX
Webinar: How Financial Firms Create a Single Customer View with MongoDB
PPTX
Jumpstart: Introduction to MongoDB
PDF
MongoDB Meetup
PPTX
Webinar: How to Drive Business Value in Financial Services with MongoDB
PDF
How Financial Services Organizations Use MongoDB
PPTX
MongoDB Days Silicon Valley: Jumpstart: The Right and Wrong Use Cases for Mon...
PPTX
MongoDB Evenings DC: MongoDB - The New Default Database for Giant Ideas
PPTX
Database Trends for Modern Applications: Why the Database You Choose Matters
PDF
Eventsourcing with PHP and MongoDB
Mongo db world 2014 billrun
MongoDB Evenings Minneapolis: MongoDB is Cool But When Should I Use It?
Single View of the Customer
Webinar: How Financial Services Organizations Use MongoDB
Webinar: Making A Single View of the Customer Real with MongoDB
Mongo db 2.4 time series data - Brignoli
MongoDB in a Mainframe World
NOSQL101, Or: How I Learned To Stop Worrying And Love The Mongo!
From SQL to MongoDB
Introduction to MongoDB at IGDTUW
How Insurance Companies Use MongoDB
Webinar: How Financial Firms Create a Single Customer View with MongoDB
Jumpstart: Introduction to MongoDB
MongoDB Meetup
Webinar: How to Drive Business Value in Financial Services with MongoDB
How Financial Services Organizations Use MongoDB
MongoDB Days Silicon Valley: Jumpstart: The Right and Wrong Use Cases for Mon...
MongoDB Evenings DC: MongoDB - The New Default Database for Giant Ideas
Database Trends for Modern Applications: Why the Database You Choose Matters
Eventsourcing with PHP and MongoDB

More from Ofer Cohen (12)

PDF
BillRun Docker Introduction
PDF
BillRun and Joomla - How is Joomla relate to billing system
PDF
Proposals, contracts and clients for web developers - Ofer Cohen
PDF
Joomla!Day Poland 2013 - Joomla Architecture (Ofer Cohen)
PDF
Joomla!Day Poland 2013 - Joomla and Open Source - How it works and how can I ...
PDF
Wordcamp Jerusalem 2013 - what if Wordpress was not open source
PDF
Joomla!Day 2013 India
PDF
Joomla!Day Israel 2012 - The business of Joomla
PDF
וורדפרס, ג'ומלה, דרופל וכל מה שביניהם‏
PDF
ג'ומלה ישראל - ותיקים מדריכים חדשים
PDF
Jab12 - Joomla! architecture revealed
PDF
Israel Joomla! 1.6 Party
BillRun Docker Introduction
BillRun and Joomla - How is Joomla relate to billing system
Proposals, contracts and clients for web developers - Ofer Cohen
Joomla!Day Poland 2013 - Joomla Architecture (Ofer Cohen)
Joomla!Day Poland 2013 - Joomla and Open Source - How it works and how can I ...
Wordcamp Jerusalem 2013 - what if Wordpress was not open source
Joomla!Day 2013 India
Joomla!Day Israel 2012 - The business of Joomla
וורדפרס, ג'ומלה, דרופל וכל מה שביניהם‏
ג'ומלה ישראל - ותיקים מדריכים חדשים
Jab12 - Joomla! architecture revealed
Israel Joomla! 1.6 Party

Recently uploaded (20)

PPTX
Programs and apps: productivity, graphics, security and other tools
PDF
gpt5_lecture_notes_comprehensive_20250812015547.pdf
PPTX
Cloud computing and distributed systems.
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PDF
Encapsulation_ Review paper, used for researhc scholars
PPTX
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
PDF
Approach and Philosophy of On baking technology
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Electronic commerce courselecture one. Pdf
PDF
NewMind AI Weekly Chronicles - August'25-Week II
PDF
A comparative analysis of optical character recognition models for extracting...
PPTX
A Presentation on Artificial Intelligence
PPTX
Machine Learning_overview_presentation.pptx
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PDF
Encapsulation theory and applications.pdf
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PDF
Unlocking AI with Model Context Protocol (MCP)
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
Programs and apps: productivity, graphics, security and other tools
gpt5_lecture_notes_comprehensive_20250812015547.pdf
Cloud computing and distributed systems.
The Rise and Fall of 3GPP – Time for a Sabbatical?
Encapsulation_ Review paper, used for researhc scholars
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
Approach and Philosophy of On baking technology
Diabetes mellitus diagnosis method based random forest with bat algorithm
Electronic commerce courselecture one. Pdf
NewMind AI Weekly Chronicles - August'25-Week II
A comparative analysis of optical character recognition models for extracting...
A Presentation on Artificial Intelligence
Machine Learning_overview_presentation.pptx
Agricultural_Statistics_at_a_Glance_2022_0.pdf
Encapsulation theory and applications.pdf
Building Integrated photovoltaic BIPV_UPV.pdf
Unlocking AI with Model Context Protocol (MCP)
Network Security Unit 5.pdf for BCA BBA.
Reach Out and Touch Someone: Haptics and Empathic Computing
“AI and Expert System Decision Support & Business Intelligence Systems”

MongoDB World 2014 - BillRun, Billing on top of MongoDB

  • 1. MongoDB World 2014 by S.D.O.C. Ltd. Billing on top of MongoDB Ofer Cohen
  • 2. Who Am I ? ● Open source evangelist ● Former Board member of OpenSourceMatters (Non-profit organization behind Joomla) ● S.D.O.C. Ltd. Co-Founder
  • 3. FUD ● FUD: Fear, Uncertainty and Doubt ● Tactic used in sales, marketing, public relations, politics and propaganda. Wikipedia
  • 4. FUD
  • 5. Preferred name for the presentation
  • 6. S.D.O.C. Ltd. vision We increase business success through great open source technologies and services ○ Open & Transparent ○ Don't reinvent the wheel ○ High Quality ○ Lean & Effective
  • 7. How did we get to billing (history) ● Client: Golan Telecom (Israel)
  • 8. How did we get to billing (history) ● Client: Golan Telecom ○ New & lean player in the market ○ 0=>~1M subscribers in few years ○ Limited resources & loves open source
  • 9. How did we get to billing (history) ● Client: Golan Telecom ○ Start up environment from Day 1 ○ Short and aggressive time to market ○ Unlimited plan for ~25$ ○ Customer can do almost everything using the website ■ Obviously requires 24/7 uptime
  • 10. How did we get to billing (history) ● Start with Anti-Fraud solution ● 2 Different data structure, 2 separated tables ○ Outgoing calls (MOC) ○ incoming calls (MTC) ○ SMS (duration=0 means SMS)
  • 11. Anti-Fraud in RDBMS How did it look like? Example code... $base_query = "SELECT imsi FROM moc WHERE callEventStartTimeStamp >=" . $start . " UNION SELECT imsi FROM mtc WHERE callEventStartTimeStamp >=" . $start $base_query = "SELECT imsi FROM (" . $base_query . ") AS qry "; if (isset($args['imsi'])) $base_query .= "WHERE imsi = '" . $this->_connection->real_escape_string($args['imsi']) . "'"; $base_query .= "GROUP BY imsi "; $mtc_join_query = "SELECT 'mtc' AS type, imsi, SUM(callEventDuration) AS duration, SUM(CEILING(callEventDuration/60)*60) AS duration_round " . ", SUM(chargeAmount) charge " . ", SUM(IF(SUBSTRING(callingNumber, 1, 3)='972', callEventDuration, IF(CHAR_LENGTH(callingNumber)<=10, callEventDuration, 0))) AS israel_duration " . ", SUM(IF(SUBSTRING(callingNumber, 1, 3)='972', CEILING(callEventDuration/60)*60, IF(CHAR_LENGTH(callingNumber)<=10, CEILING (callEventDuration/60)*60, 0))) AS israel_duration_round " . ", SUM(IF(SUBSTRING(callingNumber, 1, 3)!='972', IF(CHAR_LENGTH(callingNumber)>10, callEventDuration, 0), 0)) AS non_israel_duration " . ", SUM(IF(SUBSTRING(callingNumber, 1, 3)!='972', IF(CHAR_LENGTH(callingNumber)>10, CEILING(callEventDuration/60)*60, 0), 0)) AS non_israel_duration_round " . ", SUM(IF(callEventDuration = 0, 1, 0)) AS sms_count "
  • 12. Anti-Fraud in RDBMS How did it look like? Example code... . "FROM mtc " . "WHERE callEventStartTimeStamp >=" . $start . " " . "GROUP BY type, imsi"; $moc_join_query = "SELECT 'moc' AS type, imsi, SUM(callEventDuration) AS duration, SUM(CEILING(callEventDuration/60)*60) AS duration_round " . ", SUM(chargeAmount) charge " . ", SUM(IF(SUBSTRING(connectedNumber, 1, 3)='972', callEventDuration, IF(CHAR_LENGTH(connectedNumber)<=10, callEventDuration, 0))) AS israel_duration " . ", SUM(IF(SUBSTRING(connectedNumber, 1, 3)='972', CEILING(callEventDuration/60)*60, IF(CHAR_LENGTH(connectedNumber)<=10, CEILING (callEventDuration/60)*60, 0))) AS israel_duration_round " . ", SUM(IF(SUBSTRING(connectedNumber, 1, 3)!='972', IF(CHAR_LENGTH(connectedNumber)>10, callEventDuration, 0), 0)) AS non_israel_duration " . ", SUM(IF(SUBSTRING(connectedNumber, 1, 3)!='972', IF(CHAR_LENGTH(connectedNumber)>10, CEILING(callEventDuration/60)*60, 0), 0)) AS non_israel_duration_round " . ", SUM(IF(callEventDuration = 0, 1, 0)) AS sms_count " . "FROM moc " . "WHERE callEventStartTimeStamp >=" . $start . " " . "GROUP BY type, imsi";
  • 13. Anti-Fraud in RDBMS How did it look like? Example code... $group_query = "SELECT base.imsi, moc.duration AS moc_duration, moc.charge AS moc_charge, " . "mtc.duration AS mtc_duration, mtc.charge AS mtc_charge, " . "mtc.duration_round AS mtc_duration_round, moc.duration_round AS moc_duration_round, " . "moc.israel_duration AS moc_israel_duration, moc.non_israel_duration AS moc_non_israel_duration, " . "moc.israel_duration_round AS moc_israel_duration_round, moc.non_israel_duration_round AS moc_non_israel_duration_round, " . "mtc.israel_duration AS mtc_israel_duration, mtc.non_israel_duration AS mtc_non_israel_duration, " . "mtc.israel_duration_round AS mtc_israel_duration_round, mtc.non_israel_duration_round AS mtc_non_israel_duration_round, " . "mtc.sms_count AS mtc_sms_count, moc.sms_count AS moc_sms_count " . "FROM " . "( " . $base_query . " ) AS base " . " LEFT JOIN (" . $mtc_join_query . " ) AS mtc ON base.imsi = mtc.imsi " . " LEFT JOIN (" . $moc_join_query . " ) AS moc ON base.imsi = moc.imsi " ;
  • 14. Anti-Fraud in RDBMS How did it feel…?
  • 15. After moving to Mongo ● One main collection for 2 types (MOC & MTC) ● Aggregation framework is much more simple
  • 16. After moving to Mongo $base_match = array( '$match' => array( 'source' => 'nrtrde', 'unified_record_time' => array('$gte' => new MongoDate($charge_time)), ) ); $where = array( '$match' => array( 'record_type' => 'MOC', 'connectedNumber' => array('$regex' => '^972'), 'event_stamp' => array('$exists' => false), 'deposit_stamp' => array('$exists' => false), 'callEventDurationRound' => array('$gt' => 0), // not sms ), ); $group = array( '$group' => array( "_id" => '$imsi', "moc_israel" => array('$sum' => '$callEventDurationRound'), 'lines_stamps' => array('$addToSet' => '$stamp'), ), ); $project = array( '$project' => array( 'imsi' => '$_id', '_id' => 0, 'moc_israel' => 1, 'lines_stamps' => 1, ), );
  • 17. After moving to Mongo $having = array( '$match' => array( 'moc_israel' => array('$gte' => Billrun_Factory::config()->getConfigValue('nrtrde.thresholds.moc.israel')) ), ); $moc_israel = $lines->aggregate($base_match, $where, $group, $project, $having); //sms out to all numbers $where['$match']['record_type'] = 'MOC'; $where['$match']['callEventDurationRound'] = 0; $group['$group']['sms_out'] = $group['$group']['mtc_all']; unset($group['$group']['mtc_all']); unset($having['$match']['mtc_all']); $group['$group']['sms_out'] = array('$sum' => 1); $having['$match']['sms_out'] = array('$gte' => Billrun_Factory::config()->getConfigValue('nrtrde.thresholds.smsout')); $project['$project']['sms_out'] = 1; unset($project['$project']['mtc_all']); $sms_out = $lines->aggregate($base_match, $where, $group, $project, $having);
  • 18. What is billing? ● Group of processes of communications service providers ● responsible to collect consumption data ● calculate charging and billing information ● produce bills to customers ● process their payments and manage debt collection Wikipedia
  • 19. What is billing? ● This is how we see it ● The KISS way
  • 20. Telecom today - database challenges ● Telecom situation world wide today: ○ Unlimited packages, extend 3g usage, with high competition ○ High-volume - 4g, LTE ○ Different Events - different data structure
  • 21. 5 *main* data-structures (CDR) from different sources ● NSN - Calls ● SMSC - SMS ● MMSC - MMS ● GGSN - Data ● TAP3 - International usage Billing and MongoDB - Pros
  • 22. NSN Record > db.lines.findOne({"usaget" : "call", aid:XXXXXXX}) { "_id" : ObjectId("52bafd818f7ac3943a8b96dc"), "stamp" : "87cea5dec484c8f6a19e44e77a2e15b4", "record_type" : "01", "record_number" : "13857000", "record_status" : 0, "exchange_id" : "000006270000", "call_reference" : "700227d9a3", "urt" : ISODate("2013-12-25T15:09:15Z"), "charging_start_time" : "20131225170915", "charging_end_time" : "20131225171517", "call_reference_time" : "20131225170914", "duration" : 362, "calling_number" : "972546918666", "called_number" : "26366667", "call_type" : "3", "chrg_type" : "0", "called_imsi" : "000030000000000", "imsi" : "000089000101076", "in_circuit_group_name" : "", "in_circuit_group" : "", "out_circuit_group_name" : "NBZQZA8", "out_circuit_group" : "0503", "tariff_class" : "000000", "called_number_ton" : "6", "org_dur" : 362, "type" : "nsn", "source" : "binary", "file" : "CF0322.DAT", "log_stamp" : "0a3ffdb7c9ccc175e38be2fcc00f8c28", "process_time" : "2013-12-25 17:35:22", "usaget" : "call", "usagev" : 362, "arate" : DBRef("rates", ObjectId("521e07fcd88db0e73f0001c9")), "aid" : XXXXXXX, "sid" : YYYYY, "plan" : "LARGE", "aprice" : 0, }
  • 23. SMS Record > db.lines.findOne({"usaget" : "sms", aid:XXXXXXX}) { "_id" : ObjectId("52e52ff1d88db071648b4ad7"), "stamp" : "9328f3aaa114aaba910910053a11b3e8", "record_type" : "1", "calling_number" : "000972546918666", "calling_imsi" : "000089200000000", "calling_msc" : "000972000000000", "billable" : "000000000000000", "called_number" : "000972547655380", "called_imsi" : "425089109386379", "called_msc" : "000972586279101", "message_submition_time" : "140125192517", "time_offest" : "02", "message_delivery_time" : "140125192519", "time_offest1" : "02", "cause_of_terminition" : "100", "call_reference" : "5137864939035049", "message_length" : "050", "concatenated" : "1", "concatenated_from" : "09", "source" : "separator_field_lines", "type" : "smsc", "log_stamp" : "a5950686e364d1400c13dd1857c3340e", "file" : "140125192403_5735golan.cdr", "process_time" : "2014-01-26 17:53:21", "urt" : ISODate("2014-01-25T17:25:17Z"), "usaget" : "sms", "usagev" : 1, "arate" : DBRef("rates", ObjectId("521e07fcd88db0e73f0001db")), "aid" : XXXXXXX, "sid" : YYYYY, "plan" : "LARGE", "aprice" : 0, "usagesb" : 4, "billrun" : "201402" }
  • 24. Data Record > db.lines.findOne({"usaget" : "data", aid:XXXXXXX}) { "_id" : ObjectId("539076678f7ac34a1d8b9367"), "stamp" : "8a9f891ec85c5294c974a34653356055", "imsi" : "400009209100000", "served_imsi" : "400009209100000", "ggsn_address" : "XX.XX.144.18", "charging_id" : "2814645234", "sgsn_address" : "XX.XX.145.9", "served_pdp_address" : "XX.XX.237.95", "urt" : ISODate("2014-06-05T09:34:47Z"), "record_opening_time" : "20140605123447", "ms_timezone" : "+03:00", "node_id" : "GLTNGPT", "served_msisdn" : "00002546918666", "fbc_uplink_volume" : 61298, "fbc_downlink_volume" : 217304, "rating_group" : 0, "type" : "ggsn", "source" : "binary", "file" : "GLTNGPT_-_0000056580.20140605_-_1251+0300", "log_stamp" : "45a227ced1098bc76a44774eae04eb67", "process_time" : "2014-06-05 16:39:40", "usaget" : "data", "usagev" : 278602, "arate" : DBRef("rates", ObjectId("521e07fcd88db0e73f000200")), "apr" : 0.020264916503503202, "aid" : XXXXXXX, "sid" : YYYYY, "plan" : "LARGE", "aprice" : 0, "usagesb" : 478682116, "billrun" : "201406" }
  • 25. TAP3 Record (intl roaming) > db.lines.findOne({type:"tap3", aid:9073496}) { "_id" : ObjectId("538d9ac98f7ac3e17d8b4fd6"), "stamp" : "8f6cdc8662307ee2ed951ce640a585b5", "basicCallInformation" : { "GprsChargeableSubscriber" : { "chargeableSubscriber" : { "simChargeableSubscriber" : { "imsi" : "400009209100000" } }, "pdpAddress" : "XX.XX.227.158" }, "GprsDestination" : { "AccessPointNameNI" : "internet.golantelecom.net.il" }, "CallEventStartTimeStamp" : { "localTimeStamp" : "20140529205131", "TimeOffsetCode" : 0 }, "TotalCallEventDuration" : 163 }, "LocationInformation" : { "gprsNetworkLocation" : { "RecEntityCodeList" : { "RecEntityCode" : [ "", "u0000" ] }, "LocationArea" : 00001, "CellId" : 0001 }, "GeographicalLocation" : { "ServingNetwork" : "MMMM" } }, "ImeiOrEsn" : false, "GprsServiceUsed" : { "DataVolumeIncoming" : 195120, "DataVolumeOutgoing" : 48600, "ChargeInformationList" : { "ChargeInformation" : { "ChargedItem" : "X", "ExchangeRateCode" : 0, "ChargeDetailList" : { "ChargeDetail" : { "ChargeType" : "00", "Charge" : 001, "ChargeableUnits" : 100000, "ChargedUnits" : 100000, "ChargeDetailTimeStamp" : { "localTimeStamp" : "20140529205131", "TimeOffset" : 0 } } } } } }, "OperatorSpecInfoList" : { "OperatorSpecInformation" : [ "00000000.0000", "00000000.0000", "00000000.0000" ] }, "record_type" : "e", "urt" : ISODate("2014-05-29T18:51:31Z"), "tzoffset" : "+0200", "imsi" : "400009209100000", "serving_network" : "DEUE2", "sdr" : 0.0001, "exchange_rate" : 1.12078, "type" : "tap3", "file" : "CDBELHBISRGT02253", "log_stamp" : "a0ad109c6e795f6c1feeef9ef649d937", "process_time" : "2014-06-03 12:50:08", "usaget" : "data", "usagev" : 243720, "arate" : DBRef("rates", ObjectId ("521e07fed88db0e73f000219")), "apr" : 0.46640616, "aid" : 9073496, "sid" : 78288, "plan" : "LARGE", "out_plan" : 243720, "aprice" : 0.46640616, "usagesb" : 39139746, "billrun" : "201406" }
  • 26. Billing and MongoDB - Pros Loose coupling compared to traditional billing components Old w/o MongoDb New with MongoDb
  • 27. Billing and MongoDB - Pros ● One call can be spread on few CDRs ● BillRun merges records only on presentation layer or export ● No DB-level aggregation nor accumulation ○ No need for mediation ● Use billing and monitoring CDR in one system database
  • 28. Billing and MongoDB - Pros Sophisticated rating module ● Rating module depends on CDR structure ● Easy to implement recurring rating
  • 30. MongoDB advantages with Billing Invoice JSON2PDF process ● Use json as metadata for the PDF ● Easy to export ● Fast processing
  • 31. MongoDB advantages with Billing Invoice metadata > db.billrun.findOne({billrun_key:"201405", aid:9073496}) { "aid" : NumberLong(9073496), "subs" : [ { "sid" : NumberLong(78288), "subscriber_status" : "open", "current_plan" : DBRef("plans", ObjectId("51bd8dc9eb2f76d2178dd3dd")), "next_plan" : DBRef("plans", ObjectId("51bd8dc9eb2f76d2178dd3dd")), "kosher" : false, "breakdown" : { "in_plan" : { "base" : { "INTERNET_BILL_BY_VOLUME" : { "totals" : { "data" : { "usagev" : NumberLong(1975547725), "cost" : NumberLong(0), "count" : NumberLong(1352) } }, "vat" : 0.18 }, "IL_MOBILE" : { "totals" : { "sms" : { "usagev" : NumberLong(159), "cost" : NumberLong(0), "count" : NumberLong(159) }, "call" : { "usagev" : NumberLong(20788), "cost" : NumberLong(0), "count" : NumberLong(83) } }, "vat" : 0.18 }, "IL_FIX" : { "totals" : { "call" : { "usagev" : NumberLong(1217), "cost" : NumberLong(0), "count" : NumberLong(16) } }, "vat" : 0.18 }, "INTERNAL_VOICE_MAIL_CALL" : { "totals" : { "call" : { "usagev" : NumberLong(60), "cost" : NumberLong(0), "count" : NumberLong(2) } }, "vat" : 0.18 }, "service" : { "cost" : 83.898305085, "vat" : 0.18 } }, "intl" : { "KT_USA_NEW" : { "totals" : { "call" : { "usagev" : NumberLong(149), "cost" : NumberLong(0), "count" : NumberLong(2) } }, "vat" : 0.18 } } },
  • 32. MongoDB advantages with Billing Invoice metadata "credit" : { "refund_vatable" : { "CRM-REFUND_PROMOTION_1024-BILLRUN_201405" : -33.898305084746 } } }, "lines" : { "data" : { "counters" : { "20140425" : { "usagev" : NumberLong(11991615), "aprice" : NumberLong(0), "plan_flag" : "in" }, …. /* data by date really long, so let’s cut it from this demonstration */ } } }, "totals" : { "vatable" : 50.000000000254005, "before_vat" : 50.000000000254005, "after_vat" : 59.00000000029973 }, "costs" : { "credit" : { "refund" : { "vatable" : -33.898305084746 } }, "flat" : { "vatable" : 83.898305085 } } }, { "sid" : NumberLong(354961), "subscriber_status" : "open", "current_plan" : DBRef("plans", ObjectId("51bd8dc9eb2f76d2178dd3de")), "next_plan" : DBRef("plans", ObjectId("51bd8dc9eb2f76d2178dd3de")), "kosher" : false, "breakdown" : { "in_plan" : { "base" : { "service" : { "cost" : 8.466101695, "vat" : 0.18 } } } }, "totals" : { "vatable" : 8.466101695, "before_vat" : 8.466101695, "after_vat" : 9.9900000001 }, "costs" : { "flat" : { "vatable" : 8.466101695 } } } ], "vat" : 0.18, "billrun_key" : "201405", "totals" : { "before_vat" : 58.466101695254004, "after_vat" : 68.99000000039973, "vatable" : 58.466101695254004 }, "_id" : ObjectId("5382fd3cd88db0c31a8b74cc"), "invoice_id" : NumberLong(14738102), "invoice_file" : "201405_009073496_00014738102.xml" }
  • 33. Infrastructure BETA First Production Today Data center 1 Data center 2 Data center 1 Data center 2 Data center 1 Data center 2 Archive
  • 34. App stack BillRun application BillRun core PHP YAF framework Zend and more libraries MongoDB Web service Cli/Cron services Mongodloid https://github.com/BillRun/Mongodloid https://github.com/mongodb/mongo/ https://github.com/BillRun/system https://github.com/zendframework/ https://github.com/twbs/bootstrap https://github.com/laruence/php-yaf
  • 35. Pay Attention! What to keep in mind Transactional (tx) processes ● write concern - acknowledged (default in 2.4) ● findAndModify (A.K.A FAM) - document tx ● value=oldValue ● 2 phase commit - app side ● Transaction lock by design
  • 37. High performance tricks ● With SSD you get x20 more performance ● MongoDB loves RAM ● Follow MongoDB production notes ○ Readahead low as possible ○ THP - transparent hugepages Pay Attention! What to keep in mind
  • 38. More tips that happened to all ● Shorten property names ● MongoDB have database lock ○ Separate database for heavy-write collections Pay Attention! What to keep in mind
  • 39. TCO - MongoDB based solution ● 3 Months dev ● 3 Months QA ● Few days of maintenance infra and app ● Easy to add features ● Easy to scale
  • 40. What can BillRun do ● 5,000 events per second ○ Including DB insert and transactional rating ● That means 157,680,000,000 events per year ● Create hundreds of millions invoices
  • 42. Thank you, Ofer Cohen ofer@billrun.net @oc666 Billing on Top of