SlideShare a Scribd company logo
1 of 95
How NoSQL allows Shutl
 to deliver even faster

 database selection
 implementation problems and solution
 lessons learned
Volker Pacher
senior developer @shutl
         @vpacher
http://github.com/vpacher
• SaaS platform
• SaaS platform
• we provide an API for carriers and merchants
• SaaS platform
• we provide an API for carriers and merchants
• built on mysql, rails and ruby mri
• SaaS platform
• we provide an API for carriers and merchants
• built on mysql, rails and ruby mri
• customers can chose between a delivery either:
• SaaS platform
• we provide an API for carriers and merchants
• built on mysql, rails and ruby mri
• customers can chose between a delivery either:
   within 90 minutes of purchase
• SaaS platform
• we provide an API for carriers and merchants
• built on mysql, rails and ruby mri
• customers can chose between a delivery either:
   within 90 minutes of purchase
   or a 1 hour window of their choice
• SaaS platform
• we provide an API for carriers and merchants
• built on mysql, rails and ruby mri
• customers can chose between a delivery either:
   within 90 minutes of purchase
   or a 1 hour window of their choice
   (same day or any day)
• SaaS platform
• we provide an API for carriers and merchants
• built on mysql, rails and ruby mri
• customers can chose between a delivery either:
     within 90 minutes of purchase
     or a 1 hour window of their choice
     (same day or any day)
• fastest delivery to date 8:30 min
• SaaS platform
• we provide an API for carriers and merchants
• built on mysql, rails and ruby mri
• customers can chose between a delivery either:
     within 90 minutes of purchase
     or a 1 hour window of their choice
     (same day or any day)
• fastest delivery to date 8:30 min
• customers: Argos, Maplins, DrEd.com ...
Shutl Main Red:         Shutl Accent Red:
                                                                   Pantone 485 C           Pantone 484 C
                                                                   C0 M100 Y99 K4          C0 M100 Y99 K4
                                                                   R208 G31 B40            R208 G31 B40
red on white                                                       HEX D01F28              HEX D01F28
                                 red on lighter tones




                                                                   Shutl Black:            Shutl Accent Grey:
                                                                   Pantone BLACK           Pantone BLACK
                                                                   C0 M0 Y0 K0100          C0 M0 Y0 K70
                                                                                           @ 70%

                                                                   Please note:
Black on white                   Reverse (white) on darker tones   The black logo should never
                                                                   appear on in any ‘dark’ colour
                                                                   background.


         5 Branding Guidelines




                                                    Problems?
Shutl Main Red:         Shutl Accent Red:
                                                                   Pantone 485 C           Pantone 484 C
                                                                   C0 M100 Y99 K4          C0 M100 Y99 K4
                                                                   R208 G31 B40            R208 G31 B40
red on white                                                       HEX D01F28              HEX D01F28
                                 red on lighter tones




                                                                   Shutl Black:            Shutl Accent Grey:
                                                                   Pantone BLACK           Pantone BLACK
                                                                   C0 M0 Y0 K0100          C0 M0 Y0 K70
                                                                                           @ 70%

                                                                   Please note:
Black on white                   Reverse (white) on darker tones   The black logo should never
                                                                   appear on in any ‘dark’ colour
                                                                   background.


         5 Branding Guidelines




                                                                                                                http://xkcd.com/287/
Challenges and Problems
Challenges and Problems




• a whole zoo of np-complete problems
Challenges and Problems




• a whole zoo of np-complete problems
• exponential growth of joins in mysql with added features
Challenges and Problems




• a whole zoo of np-complete problems
• exponential growth of joins in mysql with added features
• code base too complex and unmaintanable
Challenges and Problems




• a whole zoo of np-complete problems
• exponential growth of joins in mysql with added features
• code base too complex and unmaintanable
• api response time growing to large the more data is added
The solution for v2:
The solution for v2:



build a new api on the basis of sinatra and jruby
The solution for v2:



build a new api on the basis of sinatra and jruby
databases used:
The solution for v2:



build a new api on the basis of sinatra and jruby
databases used:
 - neo4j embedded
The solution for v2:



build a new api on the basis of sinatra and jruby
databases used:
 - neo4j embedded
 - mongoDB
The solution for v2:



build a new api on the basis of sinatra and jruby
databases used:
 - neo4j embedded
 - mongoDB
 - redis
The solution for v2:



build a new api on the basis of sinatra and jruby
databases used:
 - neo4j embedded
 - mongoDB
 - redis
 - and mysql
Shutl Main Red:         Shutl Accent Red:
                                                                   Pantone 485 C           Pantone 484 C
                                                                   C0 M100 Y99 K4          C0 M100 Y99 K4
                                                                   R208 G31 B40            R208 G31 B40




                                                The case for graph databases
red on white                                                       HEX D01F28              HEX D01F28
                                 red on lighter tones




                                                                   Shutl Black:            Shutl Accent Grey:
                                                                   Pantone BLACK           Pantone BLACK
                                                                   C0 M0 Y0 K0100          C0 M0 Y0 K70
                                                                                           @ 70%

                                                                   Please note:
Black on white                   Reverse (white) on darker tones   The black logo should never
                                                                   appear on in any ‘dark’ colour
                                                                   background.


         5 Branding Guidelines
Shutl Main Red:         Shutl Accent Red:
                                                                   Pantone 485 C           Pantone 484 C
                                                                   C0 M100 Y99 K4          C0 M100 Y99 K4
                                                                   R208 G31 B40            R208 G31 B40




                                                The case for graph databases
red on white                                                       HEX D01F28              HEX D01F28
                                 red on lighter tones




                                                                   Shutl Black:            Shutl Accent Grey:
                                                                   Pantone BLACK           Pantone BLACK
                                                                   C0 M0 Y0 K0100          C0 M0 Y0 K70
                                                                                           @ 70%

                                                                   Please note:
Black on white                   Reverse (white) on darker tones   The black logo should never
                                                                   appear on in any ‘dark’ colour
                                                                   background.


         5 Branding Guidelines




                  relationships are explicit stored
Shutl Main Red:         Shutl Accent Red:
                                                                   Pantone 485 C           Pantone 484 C
                                                                   C0 M100 Y99 K4          C0 M100 Y99 K4
                                                                   R208 G31 B40            R208 G31 B40




                                                The case for graph databases
red on white                                                       HEX D01F28              HEX D01F28
                                 red on lighter tones




                                                                   Shutl Black:            Shutl Accent Grey:
                                                                   Pantone BLACK           Pantone BLACK
                                                                   C0 M0 Y0 K0100          C0 M0 Y0 K70
                                                                                           @ 70%

                                                                   Please note:
Black on white                   Reverse (white) on darker tones   The black logo should never
                                                                   appear on in any ‘dark’ colour
                                                                   background.


         5 Branding Guidelines




                  relationships are explicit stored
                  white board friendly and easier domain modeling
Shutl Main Red:         Shutl Accent Red:
                                                                   Pantone 485 C           Pantone 484 C
                                                                   C0 M100 Y99 K4          C0 M100 Y99 K4
                                                                   R208 G31 B40            R208 G31 B40




                                                The case for graph databases
red on white                                                       HEX D01F28              HEX D01F28
                                 red on lighter tones




                                                                   Shutl Black:            Shutl Accent Grey:
                                                                   Pantone BLACK           Pantone BLACK
                                                                   C0 M0 Y0 K0100          C0 M0 Y0 K70
                                                                                           @ 70%

                                                                   Please note:
Black on white                   Reverse (white) on darker tones   The black logo should never
                                                                   appear on in any ‘dark’ colour
                                                                   background.


         5 Branding Guidelines




                  relationships are explicit stored
                  white board friendly and easier domain modeling
                  schema-less
Shutl Main Red:         Shutl Accent Red:
                                                                   Pantone 485 C           Pantone 484 C
                                                                   C0 M100 Y99 K4          C0 M100 Y99 K4
                                                                   R208 G31 B40            R208 G31 B40




                                                The case for graph databases
red on white                                                       HEX D01F28              HEX D01F28
                                 red on lighter tones




                                                                   Shutl Black:            Shutl Accent Grey:
                                                                   Pantone BLACK           Pantone BLACK
                                                                   C0 M0 Y0 K0100          C0 M0 Y0 K70
                                                                                           @ 70%

                                                                   Please note:
Black on white                   Reverse (white) on darker tones   The black logo should never
                                                                   appear on in any ‘dark’ colour
                                                                   background.


         5 Branding Guidelines




                  relationships are explicit stored
                  white board friendly and easier domain modeling
                  schema-less
                  a graph is its own index
Shutl Main Red:         Shutl Accent Red:
                                                                   Pantone 485 C           Pantone 484 C
                                                                   C0 M100 Y99 K4          C0 M100 Y99 K4
                                                                   R208 G31 B40            R208 G31 B40




                                                The case for graph databases
red on white                                                       HEX D01F28              HEX D01F28
                                 red on lighter tones




                                                                   Shutl Black:            Shutl Accent Grey:
                                                                   Pantone BLACK           Pantone BLACK
                                                                   C0 M0 Y0 K0100          C0 M0 Y0 K70
                                                                                           @ 70%

                                                                   Please note:
Black on white                   Reverse (white) on darker tones   The black logo should never
                                                                   appear on in any ‘dark’ colour
                                                                   background.


         5 Branding Guidelines




                  relationships are explicit stored
                  white board friendly and easier domain modeling
                  schema-less
                  a graph is its own index
                  traversals of relationships are easy
a graph is its own index
available graph databases:



     neo4j                   (jvm)
     flockdb                  (jvm)
     DEX                     (c++)
     OrientDB                (jvm)
     Sones GraphDB           (c#)
Neo4j
Why did we chose it:
Neo4j
Why did we chose it:

it didn’t solve our np-complete problems but it solved our join hell
Neo4j
Why did we chose it:

it didn’t solve our np-complete problems but it solved our join hell
we can run it embedded in the same jvm
Neo4j
Why did we chose it:

it didn’t solve our np-complete problems but it solved our join hell
we can run it embedded in the same jvm
we can use jruby as we know ruby very well already
Neo4j
Why did we chose it:

it didn’t solve our np-complete problems but it solved our join hell
we can run it embedded in the same jvm
we can use jruby as we know ruby very well already
lots of good ruby libraries are available, we chose the neo4j gem
by Andreas Ronge (https://github.com/andreasronge/neo4j)
Neo4j
Why did we chose it:

it didn’t solve our np-complete problems but it solved our join hell
we can run it embedded in the same jvm
we can use jruby as we know ruby very well already
lots of good ruby libraries are available, we chose the neo4j gem
by Andreas Ronge (https://github.com/andreasronge/neo4j)
it speaks cypher
Neo4j
Why did we chose it:

it didn’t solve our np-complete problems but it solved our join hell
we can run it embedded in the same jvm
we can use jruby as we know ruby very well already
lots of good ruby libraries are available, we chose the neo4j gem
by Andreas Ronge (https://github.com/andreasronge/neo4j)
it speaks cypher
the guys from neotech are awesome
Neo4j
                     embedded vs. standalone
        better performance          access via rest api and
        transaction support         cypher
pros:   neo4j gem is available      language independent and
        we can use cypher and       code doesn’t need to run
        traversal                   on JVM


        only the code running the   not as performant
        db has access to the db     only works with cypher
                                    transaction is on a per
cons:
                                    query basis
                                    need to write model
                                    wrappers for ourselves
Neo4j
alternatives to the neo4j gem:


   neography by Max de Marzi
   (https://github.com/maxdemarzi/neography)

   and pacer by Darrick Wiebe:
   (https://github.com/pangloss/pacer-neo4j)
Neo4j
        gotchas and stuff we didn’t know:
Neo4j
             gotchas and stuff we didn’t know:

• testing proved to be difficult and we had to write our own tools
Neo4j
             gotchas and stuff we didn’t know:

• testing proved to be difficult and we had to write our own tools
• some libraries (like cucumber) are not compatible with jruby
Neo4j
             gotchas and stuff we didn’t know:

• testing proved to be difficult and we had to write our own tools
• some libraries (like cucumber) are not compatible with jruby
• switch from relational ‘mentality’ to a graph one was harder than
expected
Neo4j
             gotchas and stuff we didn’t know:

• testing proved to be difficult and we had to write our own tools
• some libraries (like cucumber) are not compatible with jruby
• switch from relational ‘mentality’ to a graph one was harder than
expected
• we have no real solution for migrations so far
Neo4j
             gotchas and stuff we didn’t know:

• testing proved to be difficult and we had to write our own tools
• some libraries (like cucumber) are not compatible with jruby
• switch from relational ‘mentality’ to a graph one was harder than
expected
• we have no real solution for migrations so far
• seeding an embedded database is hard
Neo4j
             gotchas and stuff we didn’t know:

• testing proved to be difficult and we had to write our own tools
• some libraries (like cucumber) are not compatible with jruby
• switch from relational ‘mentality’ to a graph one was harder than
expected
• we have no real solution for migrations so far
• seeding an embedded database is hard
• encoding Dates and Times that are stored in UTC and work across
timezone is non-trivial
Neo4j
             gotchas and stuff we didn’t know:

• testing proved to be difficult and we had to write our own tools
• some libraries (like cucumber) are not compatible with jruby
• switch from relational ‘mentality’ to a graph one was harder than
expected
• we have no real solution for migrations so far
• seeding an embedded database is hard
• encoding Dates and Times that are stored in UTC and work across
timezone is non-trivial
• nested datastructure (hashes and array) can’t be stored and need
to be converted to json
Neo4j
                            testing:

• we are using rspec for all tests on the api and practice tdd/bdd
• setting up ‘scenarios’ for an integration test was almost impossible
with existing tools
• we decided to built our own tool based on the geoff notation
developed by Nigel Small
Neo4j
                           geoff:

developed by Nigel Small (@technige, http://geoff.nigelsmall.net/)
allows modelling of graphs in a human readable form
 (A) {"name": "Alice"}
 (B) {"name": "Bob"}
 (A)-[:KNOWS]->(B)
 and provides an interface to insert them into an existing graph
Neo4j
                            geoff gem
                (https://github.com/shutl/geoff)


• provides a dsl for creating a graph and inserting it into the db
• it is open source
• it works together with FactoryGirl
(https://github.com/thoughtbot/factory_girl)
• it supports only the graph structure of the neo4j gem at the
moment
• we haven’t solved all the issues with event listeners yet
Neo4j
                     FactoryGirl
     https://github.com/thoughtbot/factory_girl
 # This will guess the User class
 FactoryGirl.define do
   factory :user do
     first_name "John"
     last_name "Doe"
     admin false
   end

   # This will use the User class (Admin would have been
 guessed)
   factory :admin, class: User do
     first_name "Admin"
     last_name "User"
     admin      true
   end
 end
#Gemfile


Neo4j                              gem 'geoff'
                                   # Basic tree like structure for DSL
                                   # the first line generates the class nodes used by
                                   Neo4jWrapper
                                   # NB 'Company' and 'Person' are classes with the
            geoff gem              Neo4j::NodeMixin
(https://github.com/shutl/geoff)   Geoff(Company, Person) do
                                     company 'Acme' do
                                       address "13 Something Road"

                                       outgoing :employees do
                                         person 'Geoff'

                                         person 'Nigel' do
                                           name 'Nigel Small'
                                         end
                                       end
                                     end

                                     company 'Github' do
                                       outgoing :customers do
                                         person 'Tom'
                                         person 'Dick'
                                         person 'Harry'
                                       end
                                     end

                                     person 'Harry' do
                                       incoming :customers do
                                         company 'NeoTech'
                                       end
                                     end
                                   end
root
                                                  node

                                 :company                          :person




                                                                  :all
       :all                                                              :all
                                  :employees              Geoff                  :all
               :all                                                                             :all
                                                                                         :all
                                                                         Nigel
   acme                                                                  Small
13 somthing
    road               :all                 :employees
                                                                                        Tom

                                                         :customers
                                                                                                Dick
              GitHub
                                                          :customers
                                                                                                       Harry

                                                           :customers

                              NeoTech
where does FactoryGirl come in?
if there are lots of attributes on a node it becomes difficult
                            to read

            Geoff(Company, Person) do
              company 'Acme' do
                address       "13 Something Road"
                contact_name “Jane Doe”
                contact_email “jane@acme.com”

                outgoing :employees do
                  person 'Geoff' do
                    first_name “Geoff”
                    last_name “Small”
                    email      “geoff@geoff.com”
                  end
                end
              end
            end
attributes can be specified in factory-girl

FactoryGirl.define do
  factory :acme, class: Company do
    address       "13 Something Road"
    contact_name “Jane Doe”
    contact_email “jane@acme.com”
  end
end

FactoryGirl.define do
  factory :geoff, class: Person do
    first_name “Geoff”
    last_name “Small”
    email      “geoff@geoff.com”
  end
end
and used in the geoff dsl


Geoff(Company, Person) do
  company 'Acme' do
    geoffactory :acme
    outgoing :employees do
      person 'Geoff' do
        geoffactory :geoff
      end
    end
  end
end




      it also works with traits
Why did we chose it:
Why did we chose it:

 very easy to get started (also on dev machines)
Why did we chose it:

 very easy to get started (also on dev machines)
 it is schemaless
Why did we chose it:

 very easy to get started (also on dev machines)
 it is schemaless
 it allows for easy sharding and horizontal scaling
Why did we chose it:

 very easy to get started (also on dev machines)
 it is schemaless
 it allows for easy sharding and horizontal scaling
 it is a json store (as our api is a json api it allows very easy storage
 of the query results)
Why did we chose it:

 very easy to get started (also on dev machines)
 it is schemaless
 it allows for easy sharding and horizontal scaling
 it is a json store (as our api is a json api it allows very easy storage
 of the query results)
 it allows easy storage of nested structure and allows for queries
 inside structure
Why did we chose it:

 very easy to get started (also on dev machines)
 it is schemaless
 it allows for easy sharding and horizontal scaling
 it is a json store (as our api is a json api it allows very easy storage
 of the query results)
 it allows easy storage of nested structure and allows for queries
 inside structure
 lots of ruby gems available (we use mongomapper, http://
 mongomapper.com/)
Why did we chose it:

 very easy to get started (also on dev machines)
 it is schemaless
 it allows for easy sharding and horizontal scaling
 it is a json store (as our api is a json api it allows very easy storage
 of the query results)
 it allows easy storage of nested structure and allows for queries
 inside structure
 lots of ruby gems available (we use mongomapper, http://
 mongomapper.com/)
 the 10gen office is 2 floors above ours
gotchas, cons and stuff we didn’t know:
gotchas, cons and stuff we didn’t know:

• it is schemaless and changed to the schema need to handled
carefully
gotchas, cons and stuff we didn’t know:

• it is schemaless and changed to the schema need to handled
carefully
• write and updated follow the ‘fire and forget’ pattern, raising an
error on save needs to be explicitly enabled
using the decorator/presenter pattern for schemaless dbs


                             decorator




                                     ‘decorates’ object for presentation




                             controller

                                                     passes decorated object to view

          retrieves object
                                                                           view



      mongo
       DB
Why did we chose it:
Why did we chose it:

fast key-value store for caching and config values
Why did we chose it:

fast key-value store for caching and config values
very easy to implement
Why did we chose it:

fast key-value store for caching and config values
very easy to implement
we have experience with it and with resque
Why did we chose it:

fast key-value store for caching and config values
very easy to implement
we have experience with it and with resque
ruby libraries are available to allow easy access and namespacing
gotchas, cons and stuff we didn’t know:
gotchas, cons and stuff we didn’t know:

• we tried to store default values for neo4j in it and found no
solution to include redis updates in a transaction
gotchas, cons and stuff we didn’t know:

• we tried to store default values for neo4j in it and found no
solution to include redis updates in a transaction
• we had some memory issues with resque that were difficult to
debug
Why are we using it:
Why are we using it:
we have lots of experience with it
Why are we using it:
we have lots of experience with it
it is very good for data aggregation (sums, groups)
Why are we using it:
we have lots of experience with it
it is very good for data aggregation (sums, groups)
ideal for financial transaction data for example
Why are we using it:
we have lots of experience with it
it is very good for data aggregation (sums, groups)
ideal for financial transaction data for example
some of our non-devs invested time to learn sql and we didn’t
want to lose the skillsets
Why are we using it:
we have lots of experience with it
it is very good for data aggregation (sums, groups)
ideal for financial transaction data for example
some of our non-devs invested time to learn sql and we didn’t
want to lose the skillsets

we have already developed the schemas for v1
Graphs are awesome!
but
chose the right database for the job
Any questions?

Volker Pacher


volker@shutl.co.uk   shutl.co.uk
@vpacher                 @shutl

More Related Content

Recently uploaded

Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 

Recently uploaded (20)

Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Top 10 Most Downloaded Games on Play Store in 2024
Top 10 Most Downloaded Games on Play Store in 2024Top 10 Most Downloaded Games on Play Store in 2024
Top 10 Most Downloaded Games on Play Store in 2024
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 

Featured

How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
ThinkNow
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
Kurio // The Social Media Age(ncy)
 

Featured (20)

2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot
 
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPT
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage Engineerings
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 

Shutl nosql exchange talk

  • 1. How NoSQL allows Shutl to deliver even faster database selection implementation problems and solution lessons learned
  • 2. Volker Pacher senior developer @shutl @vpacher http://github.com/vpacher
  • 3.
  • 4.
  • 6. • SaaS platform • we provide an API for carriers and merchants
  • 7. • SaaS platform • we provide an API for carriers and merchants • built on mysql, rails and ruby mri
  • 8. • SaaS platform • we provide an API for carriers and merchants • built on mysql, rails and ruby mri • customers can chose between a delivery either:
  • 9. • SaaS platform • we provide an API for carriers and merchants • built on mysql, rails and ruby mri • customers can chose between a delivery either: within 90 minutes of purchase
  • 10. • SaaS platform • we provide an API for carriers and merchants • built on mysql, rails and ruby mri • customers can chose between a delivery either: within 90 minutes of purchase or a 1 hour window of their choice
  • 11. • SaaS platform • we provide an API for carriers and merchants • built on mysql, rails and ruby mri • customers can chose between a delivery either: within 90 minutes of purchase or a 1 hour window of their choice (same day or any day)
  • 12. • SaaS platform • we provide an API for carriers and merchants • built on mysql, rails and ruby mri • customers can chose between a delivery either: within 90 minutes of purchase or a 1 hour window of their choice (same day or any day) • fastest delivery to date 8:30 min
  • 13. • SaaS platform • we provide an API for carriers and merchants • built on mysql, rails and ruby mri • customers can chose between a delivery either: within 90 minutes of purchase or a 1 hour window of their choice (same day or any day) • fastest delivery to date 8:30 min • customers: Argos, Maplins, DrEd.com ...
  • 14. Shutl Main Red: Shutl Accent Red: Pantone 485 C Pantone 484 C C0 M100 Y99 K4 C0 M100 Y99 K4 R208 G31 B40 R208 G31 B40 red on white HEX D01F28 HEX D01F28 red on lighter tones Shutl Black: Shutl Accent Grey: Pantone BLACK Pantone BLACK C0 M0 Y0 K0100 C0 M0 Y0 K70 @ 70% Please note: Black on white Reverse (white) on darker tones The black logo should never appear on in any ‘dark’ colour background. 5 Branding Guidelines Problems?
  • 15. Shutl Main Red: Shutl Accent Red: Pantone 485 C Pantone 484 C C0 M100 Y99 K4 C0 M100 Y99 K4 R208 G31 B40 R208 G31 B40 red on white HEX D01F28 HEX D01F28 red on lighter tones Shutl Black: Shutl Accent Grey: Pantone BLACK Pantone BLACK C0 M0 Y0 K0100 C0 M0 Y0 K70 @ 70% Please note: Black on white Reverse (white) on darker tones The black logo should never appear on in any ‘dark’ colour background. 5 Branding Guidelines http://xkcd.com/287/
  • 17. Challenges and Problems • a whole zoo of np-complete problems
  • 18. Challenges and Problems • a whole zoo of np-complete problems • exponential growth of joins in mysql with added features
  • 19. Challenges and Problems • a whole zoo of np-complete problems • exponential growth of joins in mysql with added features • code base too complex and unmaintanable
  • 20. Challenges and Problems • a whole zoo of np-complete problems • exponential growth of joins in mysql with added features • code base too complex and unmaintanable • api response time growing to large the more data is added
  • 22. The solution for v2: build a new api on the basis of sinatra and jruby
  • 23. The solution for v2: build a new api on the basis of sinatra and jruby databases used:
  • 24. The solution for v2: build a new api on the basis of sinatra and jruby databases used: - neo4j embedded
  • 25. The solution for v2: build a new api on the basis of sinatra and jruby databases used: - neo4j embedded - mongoDB
  • 26. The solution for v2: build a new api on the basis of sinatra and jruby databases used: - neo4j embedded - mongoDB - redis
  • 27. The solution for v2: build a new api on the basis of sinatra and jruby databases used: - neo4j embedded - mongoDB - redis - and mysql
  • 28. Shutl Main Red: Shutl Accent Red: Pantone 485 C Pantone 484 C C0 M100 Y99 K4 C0 M100 Y99 K4 R208 G31 B40 R208 G31 B40 The case for graph databases red on white HEX D01F28 HEX D01F28 red on lighter tones Shutl Black: Shutl Accent Grey: Pantone BLACK Pantone BLACK C0 M0 Y0 K0100 C0 M0 Y0 K70 @ 70% Please note: Black on white Reverse (white) on darker tones The black logo should never appear on in any ‘dark’ colour background. 5 Branding Guidelines
  • 29. Shutl Main Red: Shutl Accent Red: Pantone 485 C Pantone 484 C C0 M100 Y99 K4 C0 M100 Y99 K4 R208 G31 B40 R208 G31 B40 The case for graph databases red on white HEX D01F28 HEX D01F28 red on lighter tones Shutl Black: Shutl Accent Grey: Pantone BLACK Pantone BLACK C0 M0 Y0 K0100 C0 M0 Y0 K70 @ 70% Please note: Black on white Reverse (white) on darker tones The black logo should never appear on in any ‘dark’ colour background. 5 Branding Guidelines relationships are explicit stored
  • 30. Shutl Main Red: Shutl Accent Red: Pantone 485 C Pantone 484 C C0 M100 Y99 K4 C0 M100 Y99 K4 R208 G31 B40 R208 G31 B40 The case for graph databases red on white HEX D01F28 HEX D01F28 red on lighter tones Shutl Black: Shutl Accent Grey: Pantone BLACK Pantone BLACK C0 M0 Y0 K0100 C0 M0 Y0 K70 @ 70% Please note: Black on white Reverse (white) on darker tones The black logo should never appear on in any ‘dark’ colour background. 5 Branding Guidelines relationships are explicit stored white board friendly and easier domain modeling
  • 31. Shutl Main Red: Shutl Accent Red: Pantone 485 C Pantone 484 C C0 M100 Y99 K4 C0 M100 Y99 K4 R208 G31 B40 R208 G31 B40 The case for graph databases red on white HEX D01F28 HEX D01F28 red on lighter tones Shutl Black: Shutl Accent Grey: Pantone BLACK Pantone BLACK C0 M0 Y0 K0100 C0 M0 Y0 K70 @ 70% Please note: Black on white Reverse (white) on darker tones The black logo should never appear on in any ‘dark’ colour background. 5 Branding Guidelines relationships are explicit stored white board friendly and easier domain modeling schema-less
  • 32. Shutl Main Red: Shutl Accent Red: Pantone 485 C Pantone 484 C C0 M100 Y99 K4 C0 M100 Y99 K4 R208 G31 B40 R208 G31 B40 The case for graph databases red on white HEX D01F28 HEX D01F28 red on lighter tones Shutl Black: Shutl Accent Grey: Pantone BLACK Pantone BLACK C0 M0 Y0 K0100 C0 M0 Y0 K70 @ 70% Please note: Black on white Reverse (white) on darker tones The black logo should never appear on in any ‘dark’ colour background. 5 Branding Guidelines relationships are explicit stored white board friendly and easier domain modeling schema-less a graph is its own index
  • 33. Shutl Main Red: Shutl Accent Red: Pantone 485 C Pantone 484 C C0 M100 Y99 K4 C0 M100 Y99 K4 R208 G31 B40 R208 G31 B40 The case for graph databases red on white HEX D01F28 HEX D01F28 red on lighter tones Shutl Black: Shutl Accent Grey: Pantone BLACK Pantone BLACK C0 M0 Y0 K0100 C0 M0 Y0 K70 @ 70% Please note: Black on white Reverse (white) on darker tones The black logo should never appear on in any ‘dark’ colour background. 5 Branding Guidelines relationships are explicit stored white board friendly and easier domain modeling schema-less a graph is its own index traversals of relationships are easy
  • 34. a graph is its own index
  • 35. available graph databases: neo4j (jvm) flockdb (jvm) DEX (c++) OrientDB (jvm) Sones GraphDB (c#)
  • 36. Neo4j Why did we chose it:
  • 37. Neo4j Why did we chose it: it didn’t solve our np-complete problems but it solved our join hell
  • 38. Neo4j Why did we chose it: it didn’t solve our np-complete problems but it solved our join hell we can run it embedded in the same jvm
  • 39. Neo4j Why did we chose it: it didn’t solve our np-complete problems but it solved our join hell we can run it embedded in the same jvm we can use jruby as we know ruby very well already
  • 40. Neo4j Why did we chose it: it didn’t solve our np-complete problems but it solved our join hell we can run it embedded in the same jvm we can use jruby as we know ruby very well already lots of good ruby libraries are available, we chose the neo4j gem by Andreas Ronge (https://github.com/andreasronge/neo4j)
  • 41. Neo4j Why did we chose it: it didn’t solve our np-complete problems but it solved our join hell we can run it embedded in the same jvm we can use jruby as we know ruby very well already lots of good ruby libraries are available, we chose the neo4j gem by Andreas Ronge (https://github.com/andreasronge/neo4j) it speaks cypher
  • 42. Neo4j Why did we chose it: it didn’t solve our np-complete problems but it solved our join hell we can run it embedded in the same jvm we can use jruby as we know ruby very well already lots of good ruby libraries are available, we chose the neo4j gem by Andreas Ronge (https://github.com/andreasronge/neo4j) it speaks cypher the guys from neotech are awesome
  • 43. Neo4j embedded vs. standalone better performance access via rest api and transaction support cypher pros: neo4j gem is available language independent and we can use cypher and code doesn’t need to run traversal on JVM only the code running the not as performant db has access to the db only works with cypher transaction is on a per cons: query basis need to write model wrappers for ourselves
  • 44. Neo4j alternatives to the neo4j gem: neography by Max de Marzi (https://github.com/maxdemarzi/neography) and pacer by Darrick Wiebe: (https://github.com/pangloss/pacer-neo4j)
  • 45. Neo4j gotchas and stuff we didn’t know:
  • 46. Neo4j gotchas and stuff we didn’t know: • testing proved to be difficult and we had to write our own tools
  • 47. Neo4j gotchas and stuff we didn’t know: • testing proved to be difficult and we had to write our own tools • some libraries (like cucumber) are not compatible with jruby
  • 48. Neo4j gotchas and stuff we didn’t know: • testing proved to be difficult and we had to write our own tools • some libraries (like cucumber) are not compatible with jruby • switch from relational ‘mentality’ to a graph one was harder than expected
  • 49. Neo4j gotchas and stuff we didn’t know: • testing proved to be difficult and we had to write our own tools • some libraries (like cucumber) are not compatible with jruby • switch from relational ‘mentality’ to a graph one was harder than expected • we have no real solution for migrations so far
  • 50. Neo4j gotchas and stuff we didn’t know: • testing proved to be difficult and we had to write our own tools • some libraries (like cucumber) are not compatible with jruby • switch from relational ‘mentality’ to a graph one was harder than expected • we have no real solution for migrations so far • seeding an embedded database is hard
  • 51. Neo4j gotchas and stuff we didn’t know: • testing proved to be difficult and we had to write our own tools • some libraries (like cucumber) are not compatible with jruby • switch from relational ‘mentality’ to a graph one was harder than expected • we have no real solution for migrations so far • seeding an embedded database is hard • encoding Dates and Times that are stored in UTC and work across timezone is non-trivial
  • 52. Neo4j gotchas and stuff we didn’t know: • testing proved to be difficult and we had to write our own tools • some libraries (like cucumber) are not compatible with jruby • switch from relational ‘mentality’ to a graph one was harder than expected • we have no real solution for migrations so far • seeding an embedded database is hard • encoding Dates and Times that are stored in UTC and work across timezone is non-trivial • nested datastructure (hashes and array) can’t be stored and need to be converted to json
  • 53. Neo4j testing: • we are using rspec for all tests on the api and practice tdd/bdd • setting up ‘scenarios’ for an integration test was almost impossible with existing tools • we decided to built our own tool based on the geoff notation developed by Nigel Small
  • 54. Neo4j geoff: developed by Nigel Small (@technige, http://geoff.nigelsmall.net/) allows modelling of graphs in a human readable form (A) {"name": "Alice"} (B) {"name": "Bob"} (A)-[:KNOWS]->(B) and provides an interface to insert them into an existing graph
  • 55. Neo4j geoff gem (https://github.com/shutl/geoff) • provides a dsl for creating a graph and inserting it into the db • it is open source • it works together with FactoryGirl (https://github.com/thoughtbot/factory_girl) • it supports only the graph structure of the neo4j gem at the moment • we haven’t solved all the issues with event listeners yet
  • 56. Neo4j FactoryGirl https://github.com/thoughtbot/factory_girl # This will guess the User class FactoryGirl.define do factory :user do first_name "John" last_name "Doe" admin false end # This will use the User class (Admin would have been guessed) factory :admin, class: User do first_name "Admin" last_name "User" admin true end end
  • 57. #Gemfile Neo4j gem 'geoff' # Basic tree like structure for DSL # the first line generates the class nodes used by Neo4jWrapper # NB 'Company' and 'Person' are classes with the geoff gem Neo4j::NodeMixin (https://github.com/shutl/geoff) Geoff(Company, Person) do company 'Acme' do address "13 Something Road" outgoing :employees do person 'Geoff' person 'Nigel' do name 'Nigel Small' end end end company 'Github' do outgoing :customers do person 'Tom' person 'Dick' person 'Harry' end end person 'Harry' do incoming :customers do company 'NeoTech' end end end
  • 58. root node :company :person :all :all :all :employees Geoff :all :all :all :all Nigel acme Small 13 somthing road :all :employees Tom :customers Dick GitHub :customers Harry :customers NeoTech
  • 60. if there are lots of attributes on a node it becomes difficult to read Geoff(Company, Person) do company 'Acme' do address "13 Something Road" contact_name “Jane Doe” contact_email “jane@acme.com” outgoing :employees do person 'Geoff' do first_name “Geoff” last_name “Small” email “geoff@geoff.com” end end end end
  • 61. attributes can be specified in factory-girl FactoryGirl.define do factory :acme, class: Company do address "13 Something Road" contact_name “Jane Doe” contact_email “jane@acme.com” end end FactoryGirl.define do factory :geoff, class: Person do first_name “Geoff” last_name “Small” email “geoff@geoff.com” end end
  • 62. and used in the geoff dsl Geoff(Company, Person) do company 'Acme' do geoffactory :acme outgoing :employees do person 'Geoff' do geoffactory :geoff end end end end it also works with traits
  • 63.
  • 64. Why did we chose it:
  • 65. Why did we chose it: very easy to get started (also on dev machines)
  • 66. Why did we chose it: very easy to get started (also on dev machines) it is schemaless
  • 67. Why did we chose it: very easy to get started (also on dev machines) it is schemaless it allows for easy sharding and horizontal scaling
  • 68. Why did we chose it: very easy to get started (also on dev machines) it is schemaless it allows for easy sharding and horizontal scaling it is a json store (as our api is a json api it allows very easy storage of the query results)
  • 69. Why did we chose it: very easy to get started (also on dev machines) it is schemaless it allows for easy sharding and horizontal scaling it is a json store (as our api is a json api it allows very easy storage of the query results) it allows easy storage of nested structure and allows for queries inside structure
  • 70. Why did we chose it: very easy to get started (also on dev machines) it is schemaless it allows for easy sharding and horizontal scaling it is a json store (as our api is a json api it allows very easy storage of the query results) it allows easy storage of nested structure and allows for queries inside structure lots of ruby gems available (we use mongomapper, http:// mongomapper.com/)
  • 71. Why did we chose it: very easy to get started (also on dev machines) it is schemaless it allows for easy sharding and horizontal scaling it is a json store (as our api is a json api it allows very easy storage of the query results) it allows easy storage of nested structure and allows for queries inside structure lots of ruby gems available (we use mongomapper, http:// mongomapper.com/) the 10gen office is 2 floors above ours
  • 72. gotchas, cons and stuff we didn’t know:
  • 73. gotchas, cons and stuff we didn’t know: • it is schemaless and changed to the schema need to handled carefully
  • 74. gotchas, cons and stuff we didn’t know: • it is schemaless and changed to the schema need to handled carefully • write and updated follow the ‘fire and forget’ pattern, raising an error on save needs to be explicitly enabled
  • 75. using the decorator/presenter pattern for schemaless dbs decorator ‘decorates’ object for presentation controller passes decorated object to view retrieves object view mongo DB
  • 76.
  • 77. Why did we chose it:
  • 78. Why did we chose it: fast key-value store for caching and config values
  • 79. Why did we chose it: fast key-value store for caching and config values very easy to implement
  • 80. Why did we chose it: fast key-value store for caching and config values very easy to implement we have experience with it and with resque
  • 81. Why did we chose it: fast key-value store for caching and config values very easy to implement we have experience with it and with resque ruby libraries are available to allow easy access and namespacing
  • 82. gotchas, cons and stuff we didn’t know:
  • 83. gotchas, cons and stuff we didn’t know: • we tried to store default values for neo4j in it and found no solution to include redis updates in a transaction
  • 84. gotchas, cons and stuff we didn’t know: • we tried to store default values for neo4j in it and found no solution to include redis updates in a transaction • we had some memory issues with resque that were difficult to debug
  • 85.
  • 86. Why are we using it:
  • 87. Why are we using it: we have lots of experience with it
  • 88. Why are we using it: we have lots of experience with it it is very good for data aggregation (sums, groups)
  • 89. Why are we using it: we have lots of experience with it it is very good for data aggregation (sums, groups) ideal for financial transaction data for example
  • 90. Why are we using it: we have lots of experience with it it is very good for data aggregation (sums, groups) ideal for financial transaction data for example some of our non-devs invested time to learn sql and we didn’t want to lose the skillsets
  • 91. Why are we using it: we have lots of experience with it it is very good for data aggregation (sums, groups) ideal for financial transaction data for example some of our non-devs invested time to learn sql and we didn’t want to lose the skillsets we have already developed the schemas for v1
  • 93. but
  • 94. chose the right database for the job
  • 95. Any questions? Volker Pacher volker@shutl.co.uk shutl.co.uk @vpacher @shutl

Editor's Notes

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \n
  52. \n
  53. \n
  54. \n
  55. \n
  56. \n
  57. \n
  58. \n
  59. \n
  60. \n
  61. \n
  62. \n
  63. \n
  64. \n
  65. \n
  66. \n
  67. \n
  68. \n
  69. \n
  70. \n
  71. \n
  72. \n
  73. \n
  74. \n
  75. \n
  76. \n
  77. \n
  78. \n
  79. \n
  80. \n
  81. \n
  82. \n
  83. \n
  84. \n