Bongani  Ngema

Bongani Ngema

1676385720

Client-side-databases: Offline First Database Comparison

Offline First Database Comparison

In this project I have implemented the exact same chat application with different database technologies. You can use it to compare metrics and learn about the differences. The chat app is a web based angular application, with functionality similar to Whatsapp Web.

chat app

Implemented Databases:

  • AWS Amplify Datastore
  • Firebase Firestore
  • PouchDB with IndexedDB adapter & CouchDB replication
  • RxDB LokiJS with LokiJS Storage & GraphQL replication
  • RxDB Dexie.js with Dexie.js Storage & GraphQL replication
  • WatermelonDB with LokiJS adapter (no backend sync atm)

Metrics

All metrics are measured automatically via code in a browser tests (chrome:headless). The results heavily depend on the developers device. You should compare the values relative to another and not as absolute values. Also you might want to create new metrics that better represent how you would use the respective database.

You can reproduce these values by running sh measure-metrics.sh in the root folder.

Metric \ Projectawsfirebasepouchdbrxdb-dexierxdb-lokijswatermelondb
First angular component render231ms259ms219ms188ms207ms202ms
Page load time289ms207ms275ms250ms267ms259ms
First full render390ms746ms826ms473ms595ms275ms
Insert one message16ms262ms16ms18ms8ms5ms
Inserting 20 messages one after another433ms4639ms241ms223ms167ms107ms
Inserting 20 messages in parallel105ms3749ms88ms226ms37ms104ms
Message insert to message list change39ms17ms129ms18ms7ms4ms
Message search query time362ms210ms186ms37ms22ms23ms
First full render with many messages438ms852ms1288ms636ms606ms304ms
Storage usage239kb427kb1971kb1089kb2742kb2164kb
Bundle size, plain JavaScript1833kb952kb791kb1075kb1067kb955kb
Bundle size, minified+gzip421kb235kb190kb266kb254kb217kb

Metrics Explanation

  • Page load time: How long does it take to download and parse the JavaScript bundle.
  • First angular component render: How long does it take for the first angular component to be rendered.
  • First full render: How long does it take until all relevant data is displayed for the first time.
  • Insert one message: How long does it take to insert a single message.
  • Inserting 20 messages one after another: How long does it take to insert 20 messages in serial.
  • Inserting 20 messages in parallel: How long does it take to insert 20 messages in parallel.
  • Message insert to message list change: How long does it take until a new message is rendered to the dom.
  • User change to message list change: How long does it take from changing the user to the displaying of the new messages list.
  • Message search query time: How long does it take to search for a given message by regex/like-operator.
  • First full render with many messages: Time to first full render when many messages exist.
  • Storage usage: Size of the stored IndexedDB database after inserting the full test dataset.
  • Bundle size, plain JavaScript: The full JavaScript bundle size, without minification or gzip.
  • Bundle size, minified+gzip: The full JavaScript bundle size after minification and gzip compression.

Investigations

Why is LokiJS so much faster?

WatermelonDB and the RxDB-LokiJS project use the LokiJS database as storage, which is an in memory database that regularly persists the data to IndexedDB either on interval, or when the browser tab is closed. By doing so, less slow IndexedDB transaction are used. Keeping and processing the data in memory has the benefit of being much faster, but it also has its downsides:

  • Initial page load is much slower when much data is already stored in the database, because all data must be loaded before any database operation can be done.
  • All data must fit into memory.
  • Data can be lost when the JavaScript process is killed ungracefully like when the browser crashes or the power of the PC is terminated.
  • There is no multi-tab-support with plain LokiJS. The data is not shared between multiple browser tabs of the same origin. RxDB handles that by adding its own multi tab handling via the BroadcastChannel module.

Why is Firebase so slow on first render?

On the first page load, Firebase ensures that the local data is equal to the server side state. This means that the client has to be online at application startup which is the reason why Firebase is not completely offline first. To ensure the equalness of client side data, Firebase has to perform several requests to the backend, before the database will respond to queries. This makes the inital page load slow, and it becomes even more slower, the more data exists and has to be validated.

Why is PouchDB so slow?

For the PouchDB and RxDB (with PouchDB storage) I used the old Indexeddb adapter. It is much less optimized than the new adapter, but the new one made problems with returning the correct query results. Theses problems have been fixed on the PouchDB master branch, but I have to wait for the next PouchDB release. I will update the repo when this change can be done.

Why does AWS Datastore need so much less storage space?

AWS Datastore does not save any metadata together with the documents. Instead only the plain documents are stored in IndexedDB. They can do this because they only allow simple queries and do not keep a local version history.

Feature Map

Feature \ Projectawsfirebasepouchdbrxdb-lokijsrxdb-dexiewatermelondb
Offline FirstNo, login requiredPartially, must be online on first page loadYesYesYesYes
Realtime ReplicationYesYesYesYesYesPartially, must be implemented by hand
Multi Tab SupportYesYesNoYesYesPartially, relies on online sync
Observable QueriesNoYesNoYesYesYes
Complex QueriesNoYesYesYesYesYes
Client Side EncryptionNoNoYesYesYesNo
Schema SupportYesNoNoYesYesYes
Custom BackendNoNoNoYesYesYes
Custom Conflict HandlingYesNoYesYesYesNo

Starting the projects

All sub-projects use the same port and so can not be started in parallel.

Installation

  • You must have installed Node.js
  • Clone this project
  • In the root folder, run npm install to install the dependencies.
  • In the root folder, run npm run build to build all projects.

Firebase Firestore

Run npm run start:firebase to start the mock server and the production build frontend.

Or run npm run dev:firebase to start the mock server and the development frontend server.

Open http://localhost:3000/ to browse the frontend.

AWS Amplify & Datastore

The official AWS mock does not allow a live replication at this point. So you first have to setup an amplify project in the ./projects/aws folder by using this tutorial

Run npm run start:aws to start the mock server and the production build frontend.

Or run npm run dev:aws to start the mock server and the development frontend server.

Open http://localhost:3000/ to browse the frontend.

PouchDB

Run npm run start:pouchdb to start the mock server and the production build frontend.

Or run npm run dev:pouchdb to start the mock server and the development frontend server.

Open http://localhost:3000/ to browse the frontend.

RxDB LokiJS

Run npm run start:rxdb-lokijs to start the mock server and the production build frontend.

Or run npm run dev:rxdb-lokijs to start the mock server and the development frontend server.

Open http://localhost:3000/ to browse the frontend.

RxDB Dexie.js

Run npm run start:rxdb-dexie to start the mock server and the production build frontend.

Or run npm run dev:rxdb-dexie to start the mock server and the development frontend server.

Open http://localhost:3000/ to browse the frontend.

WatermelonDB

Run npm run start:watermelondb to start the mock server and the production build frontend.

Or run npm run dev:watermelondb to start the mock server and the development frontend server.

Open http://localhost:3000/ to browse the frontend.

TODOs

Pull requests are welcomed. Please help implementing more examples:

  • Meteor (with the IndexedDB offline first plugin).
  • WatermelonDB backend replication.
  • AWS Ampflify local backend mock with realtime replication.
  • GunDB.

Download Details:

Author: Pubkey
Source Code: https://github.com/pubkey/client-side-databases 
License: Apache-2.0 license

#firebase #sync #angular #database #pouchdb 

What is GEEK

Buddha Community

Client-side-databases: Offline First Database Comparison
Zachary Palmer

Zachary Palmer

1555901576

CSS Flexbox Tutorial | Build a Chat Application

Creating the conversation sidebar and main chat section

In this article we are going to focus on building a basic sidebar, and the main chat window inside our chat shell. See below.

Chat shell with a fixed width sidebar and expanded chat window

This is the second article in this series. You can check out the previous article for setting up the shell OR you can just check out the chat-shell branch from the following repository.

https://github.com/lyraddigital/flexbox-chat-app.git

Open up the chat.html file. You should have the following HTML.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Chat App</title>
    <link rel="stylesheet" type="text/css" media="screen" href="css/chat.css" />
</head>
<body>
    <div id="chat-container">
    </div>
</body>
</html>

Now inside of the chat-container div add the following HTML.

<div id="side-bar">
</div>
<div id="chat-window">
</div>

Now let’s also add the following CSS under the #chat-container selector in the chat.css file.

#side-bar {
    background: #0048AA;
    border-radius: 10px 0 0 10px;
}
#chat-window {
    background: #999;
    border-radius: 0 10px 10px 0;
}

Now reload the page. You should see the following:-

So what happened? Where is our sidebar and where is our chat window? I expected to see a blue side bar and a grey chat window, but it’s no where to be found. Well it’s all good. This is because we have no content inside of either element, so it can be 0 pixels wide.

Sizing Flex Items

So now that we know that our items are 0 pixels wide, let’s attempt to size them. We’ll attempt to try this first using explicit widths.

Add the following width property to the #side-bar rule, then reload the page.

width: 275px;

Hmm. Same result. It’s still a blank shell. Oh wait I have to make sure the height is 100% too. So we better do that too. Once again add the following property to the #side-bar rule, then reload the page.

height: 100%;

So now we have our sidebar that has grown to be exactly 275 pixels wide, and is 100% high. So that’s it. We’re done right? Wrong. Let me ask you a question. How big is the chat window? Let’s test that by adding some text to it. Try this yourself just add some text. You should see something similar to this.

So as you can see the chat window is only as big as the text that’s inside of it, and it is not next to the side bar. And this makes sense because up until now the chat shell is not a flex container, and just a regular block level element.

So let’s make our chat shell a flex container. Set the following display property for the #chat-window selector. Then reload the page.

display: flex;

So as you can see by the above illustration, we can see it’s now next to the side bar, and not below it. But as you can see currently it’s only as wide as the text that’s inside of it.

But we want it to take up the remaining space of the chat shell. Well we know how to do this, as we did it in the previous article. Set the flex-grow property to 1 on the #chat-window selector. Basically copy and paste the property below and reload the page.

flex-grow: 1;

So now we have the chat window taking up the remaining space of the chat shell. Next, let’s remove the background property, and also remove all text inside the chat-window div if any still exists. You should now see the result below.

But are we done? Technically yes, but before we move on, let’s improve things a little bit.

Understanding the default alignment

If you remember, before we had defined our chat shell to be a flex container, we had to make sure we set the height of the side bar to be 100%. Otherwise it was 0 pixels high, and as a result nothing was displayed. With that said, try removing the height property from the #side-bar selector and see what happens when you reload the page. Yes that’s right, it still works. The height of the sidebar is still 100% high.

So what happened here? Why do we no longer have to worry about setting the height to 100%? Well this is one of the cool things Flexbox gives you for free. By default every flex item will stretch vertically to fill in the entire height of the flex container. We can in fact change this behaviour, and we will see how this is done in a future article.

Setting the size of the side bar properly

So another feature of Flexbox is being able to set the size of a flex item by using the flex-basis property. The flex-basis property allows you to specify an initial size of a flex item, before any growing or shrinking takes place. We’ll understand more about this in an upcoming article.

For now I just want you to understand one important thing. And that is using width to specify the size of the sidebar is not a good idea. Let’s see why.

Say that potentially, if the screen is mobile we want the side bar to now appear across the top of the chat shell, acting like a top bar instead. We can do this by changing the direction flex items can flex inside a flex container. For example, add the following CSS to the #chat-container selector. Then reload the page.

flex-direction: column;

So as you can see we are back to a blank shell. So firstly let’s understand what we actually did here. By setting the flex-direction property to column, we changed the direction of how the flex items flex. By default flex items will flex from left to right. However when we set flex-direction to column, it changes this behaviour forcing flex items to flex from top to bottom instead. On top of this, when the direction of flex changes, the sizing and alignment of flex items changes as well.

When flexing from left to right, we get a height of 100% for free as already mentioned, and then we made sure the side bar was set to be 275 pixels wide, by setting the width property.

However now that we a flexing from top to bottom, the width of the flex item by default would be 100% wide, and you would need to specify the height instead. So try this. Add the following property to the #side-bar selector to set the height of the side bar. Then reload the page.

height: 275px;

Now we are seeing the side bar again, as we gave it a fixed height too. But we still have that fixed width. That’s not what we wanted. We want the side bar (ie our new top bar) here to now be 100% wide. Comment out the width for a moment and reload the page again.

So now we were able to move our side bar so it appears on top instead, acting like a top bar. Which as previously mentioned might be suited for mobile device widths. But to do this we had to swap the value of width to be the value of height. Wouldn’t it be great if this size was preserved regardless of which direction our items are flexing.

Try this, remove all widths and height properties from the #side-bar selector and write the following instead. Then reload the page.

flex-basis: 275px;

As you can see we get the same result. Now remove the flex-direction property from the #chat-container selector. Then once again reload the page.

Once again we are back to our final output. But now we also have the flexibility to easily change the side bar to be a top bar if we need to, by just changing the direction items can flow. Regardless of the direction of flex, the size of our side bar / top bar is preserved.

Conclusion

Ok so once again we didn’t build much, but we did cover a lot of concepts about Flexbox around sizing. 

#css #programming #webdev 

Ruth  Nabimanya

Ruth Nabimanya

1620633584

System Databases in SQL Server

Introduction

In SSMS, we many of may noticed System Databases under the Database Folder. But how many of us knows its purpose?. In this article lets discuss about the System Databases in SQL Server.

System Database

Fig. 1 System Databases

There are five system databases, these databases are created while installing SQL Server.

  • Master
  • Model
  • MSDB
  • Tempdb
  • Resource
Master
  • This database contains all the System level Information in SQL Server. The Information in form of Meta data.
  • Because of this master database, we are able to access the SQL Server (On premise SQL Server)
Model
  • This database is used as a template for new databases.
  • Whenever a new database is created, initially a copy of model database is what created as new database.
MSDB
  • This database is where a service called SQL Server Agent stores its data.
  • SQL server Agent is in charge of automation, which includes entities such as jobs, schedules, and alerts.
TempDB
  • The Tempdb is where SQL Server stores temporary data such as work tables, sort space, row versioning information and etc.
  • User can create their own version of temporary tables and those are stored in Tempdb.
  • But this database is destroyed and recreated every time when we restart the instance of SQL Server.
Resource
  • The resource database is a hidden, read only database that holds the definitions of all system objects.
  • When we query system object in a database, they appear to reside in the sys schema of the local database, but in actually their definitions reside in the resource db.

#sql server #master system database #model system database #msdb system database #sql server system databases #ssms #system database #system databases in sql server #tempdb system database

Django-allauth: A simple Boilerplate to Setup Authentication

Django-Authentication 

A simple Boilerplate to Setup Authentication using Django-allauth, with a custom template for login and registration using django-crispy-forms.

Getting Started

Prerequisites

  • Python 3.8.6 or higher

Project setup

# clone the repo
$ git clone https://github.com/yezz123/Django-Authentication

# move to the project folder
$ cd Django-Authentication

Creating virtual environment

  • Create a virtual environment for this project:
# creating pipenv environment for python 3
$ virtualenv venv

# activating the pipenv environment
$ cd venv/bin #windows environment you activate from Scripts folder

# if you have multiple python 3 versions installed then
$ source ./activate

Configured Enviromment

Environment variables

SECRET_KEY = #random string
DEBUG = #True or False
ALLOWED_HOSTS = #localhost
DATABASE_NAME = #database name (You can just use the default if you want to use SQLite)
DATABASE_USER = #database user for postgres
DATABASE_PASSWORD = #database password for postgres
DATABASE_HOST = #database host for postgres
DATABASE_PORT = #database port for postgres
ACCOUNT_EMAIL_VERIFICATION = #mandatory or optional
EMAIL_BACKEND = #email backend
EMAIL_HOST = #email host
EMAIL_HOST_PASSWORD = #email host password
EMAIL_USE_TLS = # if your email use tls
EMAIL_PORT = #email port

change all the environment variables in the .env.sample and don't forget to rename it to .env.

Run the project

After Setup the environment, you can run the project using the Makefile provided in the project folder.

help:
 @echo "Targets:"
 @echo "    make install" #install requirements
 @echo "    make makemigrations" #prepare migrations
 @echo "    make migrations" #migrate database
 @echo "    make createsuperuser" #create superuser
 @echo "    make run_server" #run the server
 @echo "    make lint" #lint the code using black
 @echo "    make test" #run the tests using Pytest

Preconfigured Packages

Includes preconfigured packages to kick start Django-Authentication by just setting appropriate configuration.

PackageUsage
django-allauthIntegrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication.
django-crispy-formsdjango-crispy-forms provides you with a crispy filter and {% crispy %} tag that will let you control the rendering behavior of your Django forms in a very elegant and DRY way.

Contributing

  • Django-Authentication is a simple project, so you can contribute to it by just adding your code to the project to improve it.
  • If you have any questions, please feel free to open an issue or create a pull request.

Download Details:
Author: yezz123
Source Code: https://github.com/yezz123/Django-Authentication
License: MIT License

#django #python 

Bongani  Ngema

Bongani Ngema

1676385720

Client-side-databases: Offline First Database Comparison

Offline First Database Comparison

In this project I have implemented the exact same chat application with different database technologies. You can use it to compare metrics and learn about the differences. The chat app is a web based angular application, with functionality similar to Whatsapp Web.

chat app

Implemented Databases:

  • AWS Amplify Datastore
  • Firebase Firestore
  • PouchDB with IndexedDB adapter & CouchDB replication
  • RxDB LokiJS with LokiJS Storage & GraphQL replication
  • RxDB Dexie.js with Dexie.js Storage & GraphQL replication
  • WatermelonDB with LokiJS adapter (no backend sync atm)

Metrics

All metrics are measured automatically via code in a browser tests (chrome:headless). The results heavily depend on the developers device. You should compare the values relative to another and not as absolute values. Also you might want to create new metrics that better represent how you would use the respective database.

You can reproduce these values by running sh measure-metrics.sh in the root folder.

Metric \ Projectawsfirebasepouchdbrxdb-dexierxdb-lokijswatermelondb
First angular component render231ms259ms219ms188ms207ms202ms
Page load time289ms207ms275ms250ms267ms259ms
First full render390ms746ms826ms473ms595ms275ms
Insert one message16ms262ms16ms18ms8ms5ms
Inserting 20 messages one after another433ms4639ms241ms223ms167ms107ms
Inserting 20 messages in parallel105ms3749ms88ms226ms37ms104ms
Message insert to message list change39ms17ms129ms18ms7ms4ms
Message search query time362ms210ms186ms37ms22ms23ms
First full render with many messages438ms852ms1288ms636ms606ms304ms
Storage usage239kb427kb1971kb1089kb2742kb2164kb
Bundle size, plain JavaScript1833kb952kb791kb1075kb1067kb955kb
Bundle size, minified+gzip421kb235kb190kb266kb254kb217kb

Metrics Explanation

  • Page load time: How long does it take to download and parse the JavaScript bundle.
  • First angular component render: How long does it take for the first angular component to be rendered.
  • First full render: How long does it take until all relevant data is displayed for the first time.
  • Insert one message: How long does it take to insert a single message.
  • Inserting 20 messages one after another: How long does it take to insert 20 messages in serial.
  • Inserting 20 messages in parallel: How long does it take to insert 20 messages in parallel.
  • Message insert to message list change: How long does it take until a new message is rendered to the dom.
  • User change to message list change: How long does it take from changing the user to the displaying of the new messages list.
  • Message search query time: How long does it take to search for a given message by regex/like-operator.
  • First full render with many messages: Time to first full render when many messages exist.
  • Storage usage: Size of the stored IndexedDB database after inserting the full test dataset.
  • Bundle size, plain JavaScript: The full JavaScript bundle size, without minification or gzip.
  • Bundle size, minified+gzip: The full JavaScript bundle size after minification and gzip compression.

Investigations

Why is LokiJS so much faster?

WatermelonDB and the RxDB-LokiJS project use the LokiJS database as storage, which is an in memory database that regularly persists the data to IndexedDB either on interval, or when the browser tab is closed. By doing so, less slow IndexedDB transaction are used. Keeping and processing the data in memory has the benefit of being much faster, but it also has its downsides:

  • Initial page load is much slower when much data is already stored in the database, because all data must be loaded before any database operation can be done.
  • All data must fit into memory.
  • Data can be lost when the JavaScript process is killed ungracefully like when the browser crashes or the power of the PC is terminated.
  • There is no multi-tab-support with plain LokiJS. The data is not shared between multiple browser tabs of the same origin. RxDB handles that by adding its own multi tab handling via the BroadcastChannel module.

Why is Firebase so slow on first render?

On the first page load, Firebase ensures that the local data is equal to the server side state. This means that the client has to be online at application startup which is the reason why Firebase is not completely offline first. To ensure the equalness of client side data, Firebase has to perform several requests to the backend, before the database will respond to queries. This makes the inital page load slow, and it becomes even more slower, the more data exists and has to be validated.

Why is PouchDB so slow?

For the PouchDB and RxDB (with PouchDB storage) I used the old Indexeddb adapter. It is much less optimized than the new adapter, but the new one made problems with returning the correct query results. Theses problems have been fixed on the PouchDB master branch, but I have to wait for the next PouchDB release. I will update the repo when this change can be done.

Why does AWS Datastore need so much less storage space?

AWS Datastore does not save any metadata together with the documents. Instead only the plain documents are stored in IndexedDB. They can do this because they only allow simple queries and do not keep a local version history.

Feature Map

Feature \ Projectawsfirebasepouchdbrxdb-lokijsrxdb-dexiewatermelondb
Offline FirstNo, login requiredPartially, must be online on first page loadYesYesYesYes
Realtime ReplicationYesYesYesYesYesPartially, must be implemented by hand
Multi Tab SupportYesYesNoYesYesPartially, relies on online sync
Observable QueriesNoYesNoYesYesYes
Complex QueriesNoYesYesYesYesYes
Client Side EncryptionNoNoYesYesYesNo
Schema SupportYesNoNoYesYesYes
Custom BackendNoNoNoYesYesYes
Custom Conflict HandlingYesNoYesYesYesNo

Starting the projects

All sub-projects use the same port and so can not be started in parallel.

Installation

  • You must have installed Node.js
  • Clone this project
  • In the root folder, run npm install to install the dependencies.
  • In the root folder, run npm run build to build all projects.

Firebase Firestore

Run npm run start:firebase to start the mock server and the production build frontend.

Or run npm run dev:firebase to start the mock server and the development frontend server.

Open http://localhost:3000/ to browse the frontend.

AWS Amplify & Datastore

The official AWS mock does not allow a live replication at this point. So you first have to setup an amplify project in the ./projects/aws folder by using this tutorial

Run npm run start:aws to start the mock server and the production build frontend.

Or run npm run dev:aws to start the mock server and the development frontend server.

Open http://localhost:3000/ to browse the frontend.

PouchDB

Run npm run start:pouchdb to start the mock server and the production build frontend.

Or run npm run dev:pouchdb to start the mock server and the development frontend server.

Open http://localhost:3000/ to browse the frontend.

RxDB LokiJS

Run npm run start:rxdb-lokijs to start the mock server and the production build frontend.

Or run npm run dev:rxdb-lokijs to start the mock server and the development frontend server.

Open http://localhost:3000/ to browse the frontend.

RxDB Dexie.js

Run npm run start:rxdb-dexie to start the mock server and the production build frontend.

Or run npm run dev:rxdb-dexie to start the mock server and the development frontend server.

Open http://localhost:3000/ to browse the frontend.

WatermelonDB

Run npm run start:watermelondb to start the mock server and the production build frontend.

Or run npm run dev:watermelondb to start the mock server and the development frontend server.

Open http://localhost:3000/ to browse the frontend.

TODOs

Pull requests are welcomed. Please help implementing more examples:

  • Meteor (with the IndexedDB offline first plugin).
  • WatermelonDB backend replication.
  • AWS Ampflify local backend mock with realtime replication.
  • GunDB.

Download Details:

Author: Pubkey
Source Code: https://github.com/pubkey/client-side-databases 
License: Apache-2.0 license

#firebase #sync #angular #database #pouchdb 

 iOS App Dev

iOS App Dev

1625133780

SingleStore: The One Stop Shop For Everything Data

  • SingleStore works toward helping businesses embrace digital innovation by operationalising “all data through one platform for all the moments that matter”

The pandemic has brought a period of transformation across businesses globally, pushing data and analytics to the forefront of decision making. Starting from enabling advanced data-driven operations to creating intelligent workflows, enterprise leaders have been looking to transform every part of their organisation.

SingleStore is one of the leading companies in the world, offering a unified database to facilitate fast analytics for organisations looking to embrace diverse data and accelerate their innovations. It provides an SQL platform to help companies aggregate, manage, and use the vast trove of data distributed across silos in multiple clouds and on-premise environments.

**Your expertise needed! **Fill up our quick Survey

#featured #data analytics #data warehouse augmentation #database #database management #fast analytics #memsql #modern database #modernising data platforms #one stop shop for data #singlestore #singlestore data analytics #singlestore database #singlestore one stop shop for data #singlestore unified database #sql #sql database