Featured

Introduction To Multithreading In C#

Threads and Memory
At its heart, multithreaded programming seems simple enough. Instead of having just one processing
unit doing work sequentially, you have two or more executing simultaneously. Because the processors
might be real hardware or might be implemented by time-multiplexing a single processor, the term
“thread” is used instead of processor. The tricky part of multithreaded programming is how threads
communicate with one another.

The most commonly deployed multithreaded communication
model is called the shared memory model.

There are four conditions needed for a race to be possible.

The first condition is that there are memory locations that are accessible from more than one thread.Typically, locations are global/static variables (as is the case of totalRequests) or are heap memory
reachable from global/static variables.

The second condition is that there is a property associated with these shared memory locations that is needed for the program to function correctly. In this case, the property is that totalRequests accurately represents the total number of times any thread has executed any part of the increment statement.Typically, the property needs to hold true (that is, totalRequests must hold an accurate count) before an update occurs for the update to be correct.


The third condition is that the property does not hold during some part of the actual update. In this particular case, from the time totalRequests is fetched until the time it is stored, totalRequests does not
satisfy the invariant.
The fourth and final condition that must occur for a race to happen is that another thread accesses the memory when the invariant is broken, thereby causing incorrect behavior.

Soluation

Locks
The most common way of preventing races is to use locks to prevent other threads from accessing memory associated with an invariant while it is broken. This removes the fourth condition I mentioned, thus making a race impossible.

The most common kind of lock goes by many different names. It is sometimes called a monitor, a

critical section, a mutex, or a binary semaphore, but regardless of the name, it provides the same basic functionality.

static object totalRequestsLock = new Object(); // executed at program
 // init
 …
 System.Threading.Monitor.Enter(totalRequestsLock);
 totalRequests = totalRequests + 1;
 System.Threading.Monitor.Exit(totalRequestsLock);

While this code does fix the race, it can introduce another problem. If an exception happens while the lock is held, then Exit will not be called. This will cause all other threads that try to run this code to block forever. In many programs, any exception would be considered fatal to the program and thus what happens in that case is not interesting. However, for programs that want to be able to recover from exceptions, the solution can be made more robust by putting the call to Exit in a finally clause:

Lock is have C# common implementation of above issues.

lock (x) { // Your code… }

object __lockObj = x;
  bool __lockWasTaken = false; 
 try 
 {    
  System.Threading.Monitor.Enter(__lockObj, ref __lockWasTaken);  
    // Your code… 
 } 
 finally
  {    
  if (__lockWasTaken) System.Threading.Monitor.Exit(__lockObj); 
 }

Avoid using the same lock object instance for different shared resources, as it might result in deadlock or lock contention

since the code uses a try…finally block, the lock is released even if an exception is thrown within the body of a lock statement.

but If exception was thrown in your code ,Allowing the program to continue without trying to fix the invariant is a bad idea

The first important observation is that locks provide mutual exclusion for regions of code, but generally programmers want to protect regions of memory. In the totalRequests example, the goal is to make certain an invariant holds true on totalRequests (a memory location). However, to do so, you actually place a lock around a region of code (the increment of totalRequests). This provides mutual exclusion over totalRequests because it is the only code that references totalRequests. If there was other code that updates totalRequests without entering the lock, then you would not have mutual exclusion for the memory, and consequently the code would have race conditions.

Make lock on as small portion of code as possible.

Taking Locks on Reads

lock for read is required to read correct value depending on implementation.

Deadlock
Another reason to avoid having many locks in the system is deadlock. Once a program has more than one lock, deadlock becomes a possibility. For example, if one thread tries to enter Lock A and then Lock B, while simultaneously another thread tries to enter Lock B and then Lock A, it is possible for them to deadlock if each enters the lock that the other owns before attempting to enter the second lock.
From a pragmatic perspective, deadlocks are generally prevented in one of two ways. The first (and best) way to prevent deadlock, is to have few enough locks in the system that it is never necessary to take more than one lock at a time.
If this is impossible, deadlock can also be prevented by having a convention on the order in which locks are taken. Deadlocks can only occur if there is a circular chain of
threads such that each thread in the chain is waiting on a lock already acquired by the next in line. To prevent this, each lock in the system is assigned a “level”, and the program is designed so that threads always take locks only in strictly descending order by level. This protocol makes cycles involving locks, and therefore deadlock, impossible. If this strategy does not work (can’t find a set of levels), it is likely
that the lock-taking behavior of the program is so input-dependent that it is impossible to guarantee that deadlock can not happen in every case. Typically, this type of code falls back on timeouts or some kind of deadlock detection scheme.
Deadlock is just one more reason to keep the number of locks in the system small. If this cannot be done, an analysis must be made to determine why more than one lock has to be taken simultaneously.
Remember, taking multiple locks is only necessary when code needs to get exclusive access to memory protected by different locks. This analysis typically either yields a trivial lock ordering that will avoid deadlock or shows that complete deadlock avoidance is impossible.

The Cost of Locks

Another reason for avoiding locks is system cost for entering and leaving lock. The special instruction used lock is expensive(typically 10 to 100 times than normal ). There are two main reasons for this expense, and they both have to do with issues arising on a true multiprocessor system.

The first reason is that the compare/exchange instruction must ensure that no other processor is also trying to do the same thing.

In addition to the raw overhead of entering and leaving locks, as the number of processors in the system grows, locks become the main impediment in using all the processors efficiently. If there are too few locks in the program, it is impossible to keep all the processors busy since they are waiting on memory that is locked by another processor.

On the other hand, if there are many locks in the program, it is easy
to have a “hot” lock that is being entered and exited frequently by many processors. This causes the memory-flushing overhead to be very high, and throughput again does not scale linearly with the number of processors. The only design that scales well is one in which worker threads can do significant work without interacting with shared data.

Inevitably, performance issues might make you want to avoid locks altogether. This can be done in certain constrained circumstances, but doing it correctly involves even more subtleties than getting mutual exclusion correct using locks. It should only be used as a last resort, and only after understanding the issues involved.

it requires a significantly higher degree of discipline to build a correct multithreaded program. Part of this discipline is ensuring through tools like monitors that all thread-shared, read-write data is protected by locks.

ref:https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Components.PostAttachments/00/10/67/06/52/MultiThreading.pdf

Google Doc Design

Functional Requirement

  • Users can change a document at the same time without any conflict
  • Users can give permission(view, editor) for a document
  • Users can make import and export a document
  • A user can add a comment to the document
  • Could be a notification (Email, GCM notification)

Design Constraint

  • Concurrency: A lot of people are working on the same document
  • Latency: People are working in different places and they connect throughout the internet, so there is a latency between each and every know clients or users when they are sharing the same document.
  • 200M User, Daily active % 10 = 20M User 
  • Average 1 MB usage ~ 1MB x 20M user = 20 BP per day usage

Google Docs users get a lot of storage space with their accounts, but it’s not unlimited. Each account can have up to:

  • 5,000 documents of up to 500 kilobytes each
  • 1,000 spreadsheets of up to 1 megabyte each
  • 5,000 presentations of up to 10 megabytes each

First, we need to understand some logic that a lot of users can change a document at the same time without any conflict.

Operational Transformation

Operational Transformations as an algorithm for automatic conflict resolution.

OT provide mechanism that allow multiple users to work on same document on same time without conflit.

OT: Transform parameter of editing operation according the effects of previously executed operations concurrent operation so that transformed object can achieve the correct effect and document consistency .

Operational Transformations as an algorithm for automatic conflict resolution

1. Introduction

Automatic conflicts resolution algorithms in distributed systems with more than one leader nodes (In this article by leader node we mean a node which accepts data changing requests) is quite interesting research field. There are different approaches and algorithms in this area with their own trade-offs and in this article we will focus on Operational Transformation technology which is aimed to solve conflicts in collaborative editing applications like Google Docs or Etherpad.

Collaborative editing is a difficult task to solve from a development point of view because clients can be doing changes in the same parts of text at almost the same time. To imitate immediate response each client has to maintain its own local copy of the document, otherwise clients would have wait till their changes are synced with other clients and that can and will take a noticeable amount of time. So the main issue in collaborative editing is to ensure consistency between local replicas i.e. all replicas have to converge to the identical, correct version of the document (Note we can not require all replicas to have an identical copy of the document at some point in time as the editing process can be endless).

1.1 First versions of Google Docs

First versions of Google Docs (as well as other similar applications) used a comparison of document versions. Imagine we have two clients — Alice and Bob and their documents are synchronized. When the server receives changes from Alice it calculates the diff between her version and its and tries to merge them. Then the server sends the merged version to Bob. If Bob has his own unsent changes he tries to merge received server’s version with his. And the process repeats.

Quite often this approach doesn’t work well. For example, imagine that Alice, Bob, and server start from the synchronized document “The quick brown fox”.

Alice bolds the words “brown fox” and at the same time Bob changes “fox” to “dog”. Alice’s changes arrived first at the server, it applies and sends changes further to Bob. The correct result of the merge should be “The quick brown dog”, but merging algorithms do not have enough information to perform the correct merge. From their perspective “The quick brown fox dog”, “The quick brown dog”, “The quick brown dog fox” are all correct and valid results. (In such cases in git you would have merge conflicts and you would have to merge manually). So that is the main problem — we can not rely on the merge with such an approach.

1.2 Latest Google Docs versions

Latest Google Docs versions follow another approach — a document is stored as a sequence of operations and they are applied in order (by order here we mean a total order) instead of comparing versions of documents.

Ok, now we need to understand when to apply changes — we need a collaboration protocol. In Google Docs all operations are down to 3 possible types:

  • Insert text
  • Delete text
  • Apply style

When you edit a document all changes are appended to the changelog in one of those 3 types and the changelog is replayed from some point when you open a document.

(First (public) Google project with OT support was Google Wave and its set of possible operations was much richer)

1.3 Example

Let Alice and Bob start from a synchronized document “LIFE 2017”.

Alice changes 2017 to 2018 and that actually creates 2 operations:

At the same time Bob changes the text to “CRAZY LIFE 2017”:

If Bob just applies Alice’s delete operation when he receives it then he gets an incorrect document (the digit 7 should have been deleted instead):

Bob needs to transform the delete operation accordingly to his local changes to get the correct state of the document :

Now it is perfect.

To make it more formal, let’s consider the following example:

then

Voila! Described algorithm is the Operational Transformation.

2. Operational Transformation

2.1 Consistency models

Few consistency models have been developed to provide consistency for OT. Let’s consider one of them — CCI model.

The CCI model consists of the following properties:

  1. Convergence: all replicas of the same document must be identical after execution of all operations

Alice and Bob start from the same document, then they do local changes and replicas diverge (thus high responsiveness is achieved). Convergence property requires Alice and Bob to see the same document after they received changes from each other.

2. Intention preservation: ensures that the effect of executing an operation on any document state will be the same as the intention of the operation. An intention of operation is defined as the effect of its execution on a replica it was created.
Let’s consider an example where this property is violated:

Alice and Bob start from the same document state, then do their local changes. The intention of Alice’s operation is to insert ‘12’ at position 1 and the intention of Bob’s operation is to delete ‘CD’. After synchronization Bob’s intention is violated. We can also observe replicas have diverged, but that is not a requirement for the intention preservation property. Exercise: what is the correct final state in this example?

3. Causality preservation: operations must be executed accordingly to their natural cause-effect order during the process of collaboration (based on the happened-before relation).
Let’s consider the example where this property is violated:

Alice, Bob, Agent Smith start from the same document state. Alice does her change and sends it to other clients. Bob receives it first and corrects the typo and sends this change to other clients. Due to a network lag, Agent Smith first receives the operation from Bob, which is to delete a symbol which doesn’t exist yet in his replica.

OT can’t ensure this property so other algorithms like Vector clock can be used.

2.2 OT Design

One of the OT design strategies is to split it into 3 parts:

  1. Transformation control algorithms: responsible for determining when an operation (transformation target) is ready for transformation, which operations (transformation reference) should be transformed against, and in which order should transformations be carried out
  2. Transformation functions: responsible for performing actual transformations on the target operation according to the impact of the reference operation
  3. Properties and conditions: define the relationships between transformation control algorithms and functions, and serve as OT algorithm correctness requirement

2.3 Transformation functions

Transformation functions can be divided into two types:

  • Inclusion/Forward transformation: to transform operation Oa before Ob so that the effect of Ob is included (e.g. two insertions)
  • Exclusion/Backward transformation: to transform operation Oa before Ob so that the effect of Ob is excluded (e.g. insertion after deletion)

Here is an example of transformation functions which work with character changes:

3. Collaboration protocol in Google Docs

Let’s consider how Google Docs collaboration protocol works in more details.

Each client maintains the following information:

  • Last synced revision (id)
  • All local changes which have not been sent to the server (Pending changes)
  • All local changes sent to the server but have not been acknowledged (Sent changes)
  • Current document state visible to the user
  • The server maintains the following information:
  • List of all received but have not been processed changes (Pending changes)
  • Log of all processed changes (Revision log)
  • State of the document on the time of last processed change
  • Assume we have Alice, Server, Bob and they start from a synchronised empty document.

High Level Design

Flow 

We have 3 clients. 

API GATEWAY :  

Is an API management tool that sits between a client and collection of backend service. 

An API Gateway takes all the API calls from clients, then routes them to the appropriate microservice with request routing, composition, and protocol translation. 

In this case, clients interact with the API GATEWAY for any operation to be performed. The gateway can connect first Authenticate and permission service whether understanding you have a permit or not in the current documents. 

Advantages 

  • Single entry point & API composition
  • Security
  • Dynamic Service Discovery
  • Service Partition HIdden
  • Hiding MS
  • Circuit Breaking

Comments Service

We might have a lot of comments about sharing documents. We can choose a line or position or a section and we can put a comment. 

The dataType could be K-V. 

K: document_id, V: Json object relevant to the comment. {line:1, position: 123, comment: “hey”, user_id: 123}

And we can use a NoSQL DB for comment service. Because we have a lot of huge data and we don’t need serious relations. K-V is enough. (Mongo, DynamoDB, etc. )

Authentication Service

Provides our permission. Could be view just to the current document or editor. We can use this information User and User Authorization DB. 

Export-Import Document Service

If you want to export or import a document this service will be in handle over here. Connecting NoSQL and RDBMS DB. It could need comments while export documents.

We have RDBMS DB. Because we need to be consistent and we need ACID property there.

Like users, even the Document ID and they refer to the document and all the different resources. 

If we have some change in a document we need ACID (Atomicity, Consistency, Isolation, Durability) or a transactional way to ensure change is completed. It didn’t complete this transaction will be very confusing matching to the diff for Operational Transformation.

Like we want to back former date documents. We make sure this transaction is complete. And we can continue some updates on the former documents. Otherwise, we can not update these former documents.

Time Series DB

Time-series data is a sequence of data points collected over time intervals, giving us the ability to track changes over time. Time-series data can track changes over milliseconds, days, or even years.

There are many other kinds of time-series data, but regardless of the scenario or use case, all time-series datasets have 3 things in common:

  1. The data that arrives is almost always recorded as a new entry
  2. The data typically arrives in time order
  3. Time is a primary axis (time-intervals can be either regular or irregular)

In other words, time-series data workloads are generally “append-only.”

Simply put: time-series datasets track changes to the overall system as INSERTs, not UPDATEs.

Some DBs: InfluxDB, ElastichSearch 

We have 3 clients and one sharing document. A user wants to reach the former 3 days ago document.  

You always make sure that there is a state which is maintained on the server because this client goes offline anytime, other clients can diverge at any time. We need to make sure we are actually maintaining the state of the document and all the histories or the updates are kind of saved in someplace. In the Time Series DB, we can get a timeline of operations, and also we can track what user has modified what particular part of the document. 

No alt text provided for this image

Websockets – TCP connection

1-) Real-Time

2-) Light Weight

HTTP, Long Pooling, AJAX not efficient. Because takes little time.

We don’t need to ask the server ‘Are there any changes.’

We just send and receive. 

When the first time a user opens the document, we actually hit the API gateway as far as if the has permission, the user will get a session established once he/she goes to the edit mode and he gets Websocket. WE can use NodeJs WebSocket which is established from the browser to the server always on so now the user can efficiently keep on sending small operations as when the user wants then receive the updates from other clients from the server immediately as soon as they broadcast it.  

We are using the Operational Transformation design in our case. We are expecting the message size to be very small but numerous events so Websockets are best for those kinds of operations even in Node Js hands as well. 

Operation Queue

All these operations should be ordered in a queue because 2 guys have to send the same operation on the same line or same char at the same time you still or the service should still prioritize one over the other so we need to it’s better to put it in the queue and then keep on in just as in when the operations from all the clients for that particular document keeps coming in. 

Session or Authorative MS

We have an operations queue here where all the operations are in the Queue over here and the session server will keep on interesting both operations and it keeps its own state of the document. That acts as a single source of truth. 

Time Series DB

Also, it keeps on saving a copy of the history of our operations into the Time Series DB. Want to deliver it back to a particular version or if you want to check who edited this particular line or this particular word we can easily get that information Time Series DB. We are using Time Series DB but if you want you can actually use SQL or another like Tree kind of representation a graph representation of how this particular line changes or document changed you can actually use graph Db as well.

Cloud Storage

If you want to convert a document to PDF or HTML or any format or if you want to share a link obviously you download it you need to talk bout it to cloud storage and that link will be provided to the user via email for them to download so you can use the cloud storage there or save that in and exporting document. 

Microservices Structure

  • Simplicty / modularity
  • Faster to develop & understand
  • Different tech stack
  • Scaling easy

Jwt Token

{header{encoded along for payload}.{payload(info about, claims(public and private)}. {Signature}

1: Jwt Token: JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.

Although JWTs can be encrypted to also provide secrecy between parties, we will focus on signed tokens. Signed tokens can verify the integrity of the claims contained within them, while encrypted tokens hide those claims from other parties. When tokens are signed using public/private key pairs, the signature also certifies that only the party holding the private key is the one that signed it.

When should you use JSON Web Tokens?

Here are some scenarios where JSON Web Tokens are useful:

  • Authorization: This is the most common scenario for using JWT. Once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token. Single Sign-On is a feature that widely uses JWT nowadays, because of its small overhead and its ability to be easily used across different domains.
  • Information Exchange: JSON Web Tokens are a good way of securely transmitting information between parties. Because JWTs can be signed—for example, using public/private key pairs—you can be sure the senders are who they say they are. Additionally, as the signature is calculated using the header and the payload, you can also verify that the content hasn’t been tampered with.

Three main components:

1: Header

2:Payload

3: Verify Signature

Therefore, a JWT typically looks like the following.

xxxxx.yyyyy.zzzzz

Header

The header typically consists of two parts: the type of the token, which is JWT, and the signing algorithm being used, such as HMAC SHA256 or RSA.

For example:

{
  "alg": "HS256",
  "typ": "JWT"
}

Then, this JSON is Base64Url encoded to form the first part of the JWT.

Payload

The second part of the token is the payload, which contains the claims. Claims are statements about an entity (typically, the user) and additional data. There are three types of claims: registeredpublic, and private claims.

  • Registered claims: These are a set of predefined claims which are not mandatory but recommended, to provide a set of useful, interoperable claims. Some of them are: iss (issuer), exp (expiration time), sub (subject), and (audience), and others
    • .Notice that the claim names are only three characters long as JWT is

Payload

The second part of the token is the payload, which contains the claims. Claims are statements about an entity (typically, the user) and additional data. There are three types of claims: registeredpublic, and private claims.

  • Registered claims: These are a set of predefined claims which are not mandatory but recommended, to provide a set of useful, interoperable claims. Some of them are iss (issuer), exp (expiration time), sub (subject), and (audience), and others. Notice that the claim names are only three characters long as JWT is

Payload

The second part of the token is the payload, which contains the claims. Claims are statements about an entity (typically, the user) and additional data. There are three types of claims: registeredpublic, and private claims.

  • Registered claims: These are a set of predefined claims which are not mandatory but recommended, to provide a set of useful, interoperable claims. Some of them are iss (issuer), exp (expiration time), sub (subject), and (audience), and others.
  • Notice that the claim names are only three characters long as JWT is meant to be compact.
  • Public claims: These can be defined at will by those using JWTs. But to avoid collisions they should be defined in the IANA JSON Web Token Registry or be defined as a URI that contains a collision-resistant namespace.
  • Private claims: These are the custom claims created to share information between parties that agree on using them and are neither registered nor public claims.

An example payload could be:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

For example, if you want to use the HMAC SHA256 algorithm, the signature will be created in the following way:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

The signature is used to verify the message wasn’t changed along the way, and, in the case of tokens signed with a private key, it can also verify that the sender of the JWT is who it says it is.

The output is three Base64-URL strings separated by dots.

The following shows a JWT that has the previous header and payload encoded, and it is signed with a secret. 

Encoded JWT
How does a JSON Web Token work
The application or client requests authorization to the authorization server. This is performed through one of the different authorization flows. For example, a typical OpenID Connect compliant web application will go through the /oauth/authorize endpoint using the authorization code flow.
When the authorization is granted, the authorization server returns an access token to the application.
The application uses the access token to access a protected resource (like an API).

JSON parsers are common in most programming languages because they map directly to objects. Conversely, XML doesn’t have a natural document-to-object mapping. This makes it easier to work with JWT than SAML assertions.

Mongo DB Compound Index Creation Guide:

Indexes

Indexes support the efficient execution of queries in MongoDB. Without indexes, MongoDB must perform a collection scan, i.e. scan every document in a collection, to select those documents that match the query statement. If an appropriate index exists for a query, MongoDB can use the index to limit the number of documents it must inspect.

Creating an Index for a Collection

Assuming you have inserted some data in your collection and you want to assign a field to be an index, you can use the createIndex method to achieve this, i.e.

Let’s say you have this json data:

12345{    _id:1,    Name: “Sepp Maier”,     Country: “Germany”}

We can make the Name field a descending index by:

1db.collection.createIndex({Name: -1})

This method creates an index with the same specification if only not in existence already.

Types of Indexes in MongoDB

  1. Single FieldUsing a single field of a document one can make the field an index in an ascending or descending manner just like the example above. Besides, you can create an index on an embedded document as a whole, for example
  2. Contact field is an embedded document hence we can make it an ascending index with the command:

{     _id: “xyz”,    Contact:{        email: “example@gmail.com”,         phone:”+420 78342823” },    Name: “Sergio”}

  1. 1db.collection.createIndex({ Contact: 1})In a query we can fetch the document like:
  2. db.collection.find({     Contact: {email: “example@gmail.com”,    phone:”+420 78342823”} })A best practice is creating the index in the background especially when a large amount of data is involved since the application needs to access the data while building the index.
  3. Compound Index: Compound indexes are often used to facilitate the sort operation within a query and support queries that match on multiple fields. The syntax for creating a compound index is:1db.collection.createIndex( { <field0>: <type>, <field1>: <type1>, ... } )Creating a compound index for the sample data below1234567{     _id: “1”,    Name: “Tom”,    Age: 24,    Score:”80”}db.collection.createIndex({ Age: 1, Score:-1})
  4. Considerations:
    • A limit of only 32 fields can be supported.
    • Value of the field will define the type of index i.e. 1 is ascending and -1 is descending.
    • Don’t create compound indexes that have hashed index type.
    • The order of fields listed in a compound index is important. The sorting will be done in accordance with the order of the fields.
  5. Multikey IndexAt some point, you may have fields with stored array content. When these fields are indexed, separate index entries for every element are created. It therefore helps a query to select documents that consist arrays by matching on element or elements of the arrays. This is done automatically by MongoDB hence no need for one to explicitly specify the multikey type. From version 3.4, MongoDB tracks which indexed fields cause an index to be a multikey index. With this tracking, the database query engine is allowed to use tighter index bounds.Limitations of Multikey Index
    • Only one array field can be used in the multikey indexing for a document in the collection. I.e. You cannot create a multikey index for the command and data below1

MongoDB involves different types of data hence different types of indexes are derived to support these data types and queries.

  • Only one array field can be used in the multikey indexing for a document in the collection. I.e. You cannot create a multikey index for the command and data below1{ _id: 1, nums: [ 1, 2 ], scores: [ 30, 60 ]}You cannot create a multikey index1{ nums: 1, scores: 1 }
  • If the multikey index already exists, you cannot insert a document that violates this restriction. This is to say if we have12{ _id: 1, nums:  1, scores: [ 30, 60 ]}{ _id: 1, nums: [ 1, 2 ], scores:  30}After creating a compound multikey index, an attempt to insert a document where both nums and scores fields are arrays, the database will fail the insert.
  • After creating a compound multikey index, an attempt to insert a document where both nums and scores fields are arrays, the database will fail the insert.

Overall Operational Considerations for Indexes

  • Each index requires at least 8kB of data space.
  • When active, each index will consume some disk space and memory. This is significant when tracked in capacity planning.
  • For a high read-to-write ratio collection, additional indexes improve performance and do not affect un-indexed read operations.

Limitations of Using Indexes

  • Adding an index has some negative performance impact for write operations especially for collections with the high write-to-read ratio. Indexes will be expensive in that each insert must also update any index.
  • MongoDB will not create, update an index or insert into an indexed collection if the index entry for an existing document exceeds the index key limit.
  • For existing sharded collections, chunk migration will fail if the chunk has a document that contains an indexed field that has an index entry that exceeds the index key limit.

Conclusion

There are so many ways of improving MongoDB performance, indexing being one of them. Indexing facilitates query operations by reducing latency over which data is retrieved by somehow minimizing the number of documents that need to be scanned. However, there are some considerations one needs to undertake before deciding to use a specific type of index. Collections with high read-to-write ratio tend to utilize indexes better than collections with high write-to-read operations.

How does the order of compound indexes matter in MongoDB performance-wise?

Redsandro,

You must consider Index Cardinality and Selectivity.


1. Index Cardinality

The index cardinality refers to how many possible values there are for a field. The field sex only has two possible values. It has a very low cardinality. Other fields such as names, usernames, phone numbers, emails, etc. will have a more unique value for every document in the collection, which is considered high cardinality.

  • Greater CardinalityThe greater the cardinality of a field the more helpful an index will be, because indexes narrow the search space, making it a much smaller set.If you have an index on sex and you are looking for men named John. You would only narrow down the result space by approximately %50 if you indexed by sex first. Conversely if you indexed by name, you would immediately narrow down the result set to a minute fraction of users named John, then you would refer to those documents to check the gender.
  • Rule of ThumbTry to create indexes on high-cardinality keys or put high-cardinality keys first in the compound index. You can read more about it in the section on compound indexes in the book:MongoDB The Definitive Guide

2. Selectivity

Also, you want to use indexes selectively and write queries that limit the number of possible documents with the indexed field. To keep it simple, consider the following collection. If your index is {name:1}, If you run the query { name: "John", sex: "male"}. You will have to scan 1 document. Because you allowed MongoDB to be selective.

{_id:ObjectId(),name:"John",sex:"male"}
{_id:ObjectId(),name:"Rich",sex:"male"}
{_id:ObjectId(),name:"Mose",sex:"male"}
{_id:ObjectId(),name:"Sami",sex:"male"}
{_id:ObjectId(),name:"Cari",sex:"female"}
{_id:ObjectId(),name:"Mary",sex:"female"}

Consider the following collection. If your index is {sex:1}, If you run the query {sex: "male", name: "John"}. You will have to scan 4 documents.

{_id:ObjectId(),name:"John",sex:"male"}
{_id:ObjectId(),name:"Rich",sex:"male"}
{_id:ObjectId(),name:"Mose",sex:"male"}
{_id:ObjectId(),name:"Sami",sex:"male"}
{_id:ObjectId(),name:"Cari",sex:"female"}
{_id:ObjectId(),name:"Mary",sex:"female"}

Imagine the possible differences on a larger data set.


A little explanation of Compound Indexes

It’s easy to make the wrong assumption about Compound Indexes. According to MongoDB docs on Compound Indexes.

MongoDB supports compound indexes, where a single index structure holds references to multiple fields within a collection’s documents. The following diagram illustrates an example of a compound index on two fields:

enter image description here

When you create a compound index, 1 Index will hold multiple fields. So if we index a collection by {"sex" : 1, "name" : 1}, the index will look roughly like:

["male","Rick"] -> 0x0c965148
["male","John"] -> 0x0c965149
["male","Sean"] -> 0x0cdf7859
["male","Bro"] ->> 0x0cdf7859
...
["female","Kate"] -> 0x0c965134
["female","Katy"] -> 0x0c965126
["female","Naji"] -> 0x0c965183
["female","Joan"] -> 0x0c965191
["female","Sara"] -> 0x0c965103

If we index a collection by {"name" : 1, "sex" : 1}, the index will look roughly like:

["John","male"] -> 0x0c965148
["John","female"] -> 0x0c965149
["John","male"] -> 0x0cdf7859
["Rick","male"] -> 0x0cdf7859
...
["Kate","female"] -> 0x0c965134
["Katy","female"] -> 0x0c965126
["Naji","female"] -> 0x0c965183
["Joan","female"] -> 0x0c965191
["Sara","female"] -> 0x0c965103

Having {name:1} as the Prefix will serve you much better in using compound indexes. There is much more that can be read on the topic, I hope this can offer some clarity.

Web Server

web server is computer software and underlying hardware that accepts requests via HTTP, the network protocol created to distribute web pages, or its secure variant HTTPS.

Basic common features[edit]

Although web server programs differ in how they are implemented, most of them offer the following basic common features.

  • HTTP: support for one or more versions of HTTP protocol in order to send versions of HTTP responses compatible with versions of client HTTP requests, e.g. HTTP/1.0, HTTP/1.1 plus, if available, HTTP/2HTTP/3;
  • Logging: usually web servers have also the capability of logging some information, about client requests and server responses, to log files for security and statistical purposes.

A few other popular features (only a very short selection) are:

Async Method Return Types at a Glance

Methods marked with async in C# must return one of the following:

  • Task
  • Task<T>
  • ValueTask
  • ValueTask<T>
  • void

Why You Should Generally Stick to Task

If you feel overwhelmed by the list of return types above, let me put your mind at ease. Most of the time, you should just use a return type of Task or Task<T> with async methods. Everything else in the list above is for specific situations that are not very common.

If anything, you have a choice between returning Task and returning void. But even that choice is lopsided, heavily favoring the use of Task. Why? If a method returns void, callers of that method are not allowed to await it. And if you don’t await a method, execution of the caller may continue before the method completes. Even more problematic is that the caller can’t handle exceptions properly when it does not await an async method. There are a few valid reasons for returning void from an async method, but the vast majority of the time you should return a Task so you can await it.

Allowed Parameter Types in Async Methods

One of the reasons that Task<T> is needed to pass back data to the caller is that async methods are not allowed to have ref or out parameters.

The in parameter modifier, introduced in C# 7.2, is also disallowed in async methods.

For example, the following would cause a compiler error:

async Task<bool> TryGetHtml(string url, out string html)

That being the case, you can’t return data using ref or out parameters. Really the only way to return data from an async method is using Task<T>. But the nice thing is that T can be literally anything. It can be a value type such as int or bool, or any reference type, including collections, arrays, or your own custom class. If you find yourself wanting to return multiple variables from an async method, you can define a class that will contain everything you need and return an instance of that class, or, if that proves inconvenient, you can return a Tuple<T1, T2>. As an example, we could implement what we were attempting earlier with TryGetHtml as follows:


static async Task<(bool, string)> TryGetHtml(string url)
{
  if (string.IsNullOrEmpty(url))
  {
    return (false, null);
  }
  string html = await new HttpClient().GetStringAsync(url);
  return (true, html);
}

And we could call such a method using:


(bool success, string html) = await TryGetHtml("http://...");
if (success)
{
  // do something with html
}  

You can even return a Task<Task<T>> from an async method, which allows nesting of tasks and is occasionally useful. So don’t worry about the inability to use ref and out parameters with async methods. Using Task<T> gives you everything you need!

When to Use ValueTask Instead of Task

So what about ValueTask and ValueTask<T>? Those two are wrappers around Task and Task<T>, with the distinction that they are defined using a struct instead of a class.

To understand why that might be useful, keep in mind that a class instance is a reference to data that lives in long-term memory (which must be allocated), while a struct is a value type whose data lives in short-term memory (which does not require allocation). Since long-term memory allocation can be expensive, using a ValueTask can sometimes yield better performance. That said, a ValueTask wraps a Task, so when the ValueTask‘s Task field is populated, not only does it still involve long-term memory, it also uses more memory overall than a Task by itself, which actually ends up being worse. So it depends very much on the application.

The good news is that most developers do not need to concern themselves with the details. The general recommendation is to always use Task or Task<T>, and only consider using ValueTask or ValueTask<T> if profiling your code with a performance analysis tool indicates that the allocations associated with Task are a bottleneck for your particular application. As it turns out, this would only be the case if your code was structured asynchronously, but executes synchronously (due to cached results, or frequent use of methods like Task.FromResult<T> that generate pre-completed Task instances), and does so in tight loops.

So while it’s certainly nice to have ValueTask available just in case, it’s unlikely you will ever need it. If you do end up using ValueTask, be sure to read all available documentation carefully, as its behavior can differ from Task in subtle ways.

Conclusion

While there are a number of return types compatible with async methods in C#, there are fortunately just two main options, namely Task and Task<T>. You will find that the C# compiler will help you choose between those two depending on the needs of the method you’re writing.

What can be a bit more challenging is understanding when it is appropriate to return void from an async method. The next guide in this series will explore this topic in detail.

Singleton Vs Static Classes

1:sigleton classes support Interface inheritance whereas a static class cannot implement an interface:

2:

  1. public class Singleton  
  2. {  
  3.    private static Singleton instance;  
  4.    
  5.    private Singleton() {}  
  6.    
  7.    public static Singleton Instance  
  8.    {  
  9.       get  
  10.       {  
  11.          if (instance == null)  
  12.          {  
  13.             instance = new Singleton();  
  14.          }  
  15.          return instance;  
  16.       }  
  17.    }  
  18. }
  19.    
  20.     static public class StaticSampleClass  
  21.     {  
  22.         private static readonly int SomeVariable;  
  23.         //Static constructor is executed only once when the type is first used.   
  24.         //All classes can have static constructors, not just static classes.  
  25.         static StaticSampleClass()  
  26.         {  
  27.             SomeVariable = 1;  
  28.             //Do the required things  
  29.         }  
  30.         public static string ShowValue()  
  31.         {  
  32.             return string.Format(“The value of someVariable is {0}”, SomeVariable);  
  33.         }  
  34.         public static string Message { getset; }  
  35.     }  

When shall we use singleton class and when to go for static classes?

Static classes are basically used when you want to store a single instance, data which should be accessed globally throughout your application. The class will be initialized at any time but mostly it is initialized lazily. Lazy initialization means it is initialized at the last possible moment of time. There is a disadvantage of using static classes. You never can change how it behaves after the class is decorated with the static keyword.

Singleton Class instance can be passed as a parameter to another method whereas static class cannot

Singleton Class instance can be passed as a parameter to another method whereas static class cannot

Static Initialization

  1. public sealed class Singleton  
  2. {  
  3.    private static readonly Singleton instance = new Singleton();  
  4.     
  5.    private Singleton(){}  
  6.    
  7.    public static Singleton Instance  
  8.    {  
  9.       get  
  10.       {  
  11.          return instance;  
  12.       }  
  13.    }  
  14. }  

Multithreaded Singleton

  1. public sealed class Singleton  
  2. {  
  3.    private static volatile Singleton instance;  
  4.    private static object syncRoot = new Object();  
  5.    
  6.    private Singleton() {}  
  7.    
  8.    public static Singleton Instance  
  9.    {  
  10.       get  
  11.       {  
  12.          if (instance == null)  
  13.          {  
  14.             lock (syncRoot)  
  15.             {  
  16.                if (instance == null)  
  17.                   instance = new Singleton();  
  18.             }  
  19.          }  
  20.    
  21.          return instance;  
  22.       }  
  23.    }  
  24. }

Resulting Context

Implementing Singleton in C# results in the following benefits and liabilities:

Benefits

  • The static initialization approach is possible because the .NET Framework explicitly defines how and when static variable initialization occurs.
  • The Double-Check Locking idiom described earlier in “Multithreaded Singleton” is implemented correctly in the Common Language Runtime.

Liabilities

If your multithreaded application requires explicit initialization, you have to take precautions to avoid threading issues.

Improving scalability in C# using Async and Await

Asynchronous code targets scalability making us use the resources of our servers better.

The inspiration for this article comes from the talk given by
Maarten Balliauw.

What is asynchronous code good for?

Asynchronous programming can in some cases help with performance by parallelizing a task. But, that is not its main benefit in day to day development.

Instead, the main benefit comes from making our code more scalable. The scalability feature of a system relates to how it handles a growing amount of work. And its potential to scale to accommodate that growth.

Soluation

1:We can use horizontal scaling no matter if we have synchronous or asynchronous code. There is no difference.

2:Vertical scaling,Another way of scaling is to improve how many requests our server can handle. Every single request will most likely not perform any better. But, we can increase the number of concurrent requests the server can handle.

Some useful tips:

1: Once the thread is waiting on await, thread will be returned to thread poll.
2:: cancelling the task when one of task throw exception when parallel how handle that
3:; synchronization Context: asp .net have asp .net core doesn’t have
Task.Result() doesn’t deadlock but may block thread .
we don’t need configureAwait(false) is not necessary because there is no synchronization context.
if you think your library might be use by full dotent.
5: we often need to communicate multiple api
using these call parallel.

List tasks = new ListTask ( GetBOOK1Async,GetBOOK2Async)
try
{
return await Task.WhenALL(tasks);
}
catch(operationCanceledException excepation)
{
http client will throw Opertion canceled exception
forearch( var task in tasks
{
logger.Log($”task with task Id: {task.Id} has been canceled”};
}
}

catch(excepation ex)
{
}
cancelationToken: most calls asp .net core that work with task can be canceled.
make sure cancel task manually but ask http client to cancel the task.
var response = await httpclient.GetAsync(bookcoverUrl, cancellationToken);
(rsponse.IsSuccessCode)
{
var bookCover = JsonConverter.DeseralizeObject(
await rersponse.Content.ReadAsync());
return bookcover;
}
_cancellationToken.cancel();
return null;

}

when one of call canceled we should cancel all the task.

Which work should we use async for?

Async is not the solution to all thread blocking code. Threads block if we call a method that performs some heavy computation or if it needs to wait for IO. It is only in the case of IO that we get any gain from using async.

IO bound

Any task that accesses the file system or any external service is good candidates for async.

Computational bound

Any algorithm that takes a long time and is CPU or memory intensive are bad candidates for async.

Examples

Adding an entity using entityframework: IO happens when we call save. Not when we initially add the entity. It is not a good candidate for using async. Entityframework supplies us with an async add method, but we gain nothing from using it.

ASP.NET Example

Asp .net b

3

k

Sync vs. Async vs. Concurrent vs. Parallel

“How do you distinguish between sync vs. async vs. concurrent vs. parallel?”

It’s a question you’ll probably be asked in your first technical interview.

Having witnessed a lot of answers from interviewees, I see that people know the terms, but they rarely understand what they conceptually are.

Knowing use cases are essential. However, just knowing the use cases also limits yourself to only those use cases. That’s why interviewers want to ask you this question⁠ — they want to see whether you’re able to introduce solutions for new use cases.

Now, let’s break the code.

Sync vs. Async

Sync and async are two different programming models, which refer to styles of programming, how you should write code, and how your code will run.

In the sync programming model, you write code as steps ⁠— your code is executed from top to bottom, step by step, and it only gets to the second step when it has finished the first step.

func step1() { print("1") }
func step2() { print("2") }func main() {
step1()
step2()
}// Result: "12"

Because of its predictable behavior, sync is also called a predictable programming model. Most programming languages use sync as its base programming model.

In an async programming model, you write code as tasks, which are then executed concurrently.Executing concurrently means that all the tasks are likely executed at the same time.

func task1() { print("1") }
func task2() { print("2") }func main() {
task1()
task2()
}// Result: "12" or "21" (not predictable)

As you can see in the result of the example, because tasks are executed at the same time you can not predict the result. And due to that behavior, async is also called an unpredictable programming model. It’s useful for use cases in which you do tasks that do not depend on each other and for which you don’t care about the execution order.

Concurrent vs. Parallel

We mentioned concurrent behaviors once when discussing the async programming model. In an async programming model, tasks are treated as a single step that runs multiple tasks, and they do not care about how those tasks are ordered or run to each other.They can be run simultaneously, or, in some cases, there’ll be some tasks that run first and then pause and other tasks that come in turns, etc. That behavior is called concurrent.

Take an example in real life: There’s a challenge that requires you to both eat a whole huge cake and sing a whole song. You’ll win if you’re the fastest who sings the whole song and finishes the cake. So the rule is that you sing and eat concurrently. How you do that does not belong to the rule. You can eat the whole cake, then sing the whole song, or you can eat half a cake, then sing half a song, then do that again, etc.

The same applies to computer science. There are two tasks executing concurrently, but those are run in a 1-core CPU, so the CPU will decide to run a task first and then the other task or run half a task and half another task, etc. It all depends on the system architecture.

If we keep going with the same example, the rule is still singing and eating concurrentlybut this time, you play in a team of two. You probably will eat and let your friend sing (because she sings better and you eat better). So this time, the two tasks are really executed simultaneously, and it’s called parallelParallelism is a specific kind of concurrency where tasks are really executed simultaneously. In computer science, parallelism can only be achieved in multicore environments.

Summary

Sync and async are programming models.

Concurrent and parallel are ways tasks are executed, where parallel is a narrow version of concurrent.

In sync, you write code as steps that are executed in order, from top to bottom. There’s no concurrency or parallelism here.

In async, you write code as tasks which are executed concurrently. You never know which tasks will be started first — it depends on the executing context, whether you run tasks in parallel or do a bit of one task then progress to another.

Long-Polling vs WebSockets vs Server-Sent Events

Ajax Polling
HTTP Long-Polling
WebSockets
Server-Sent Events (SSEs)

Ajax Polling#

Polling is a standard technique used by the vast majority of AJAX applications. The basic idea is that the client repeatedly polls (or requests) a server for data. The client makes a request and waits for the server to respond with data. If no data is available, an empty response is returned.

  1. The client opens a connection and requests data from the server using regular HTTP.
  2. The requested webpage sends requests to the server at regular intervals (e.g., 0.5 seconds).
  3. The server calculates the response and sends it back, just like regular HTTP traffic.
  4. The client repeats the above three steps periodically to get updates from the server.

HTTP Long-Polling

This is a variation of the traditional polling technique that allows the server to push information to a client whenever the data is available. With Long-Polling, the client requests information from the server exactly as in normal polling, but with the expectation that the server may not respond immediately. That’s why this technique is sometimes referred to as a “Hanging GET”.

  • If the server does not have any data available for the client, instead of sending an empty response, the server holds the request and waits until some data becomes available.
  • Once the data becomes available, a full response is sent to the client. The client then immediately re-request information from the server so that the server will almost always have an available waiting request that it can use to deliver data in response to an event.

WebSockets#

WebSocket provides Full duplex communication channels over a single TCP connection. It provides a persistent connection between a client and a server that both parties can use to start sending data at any time. The client establishes a WebSocket connection through a process known as the WebSocket handshake. If the process succeeds, then the server and client can exchange data in both directions at any time. The WebSocket protocol enables communication between a client and a server with lower overheads, facilitating real-time data transfer from and to the server. This is made possible by providing a standardized way for the server to send content to the browser without being asked by the client and allowing for messages to be passed back and forth while keeping the connection open. In this way, a two-way (bi-directional) ongoing conversation can take place between a client and a server.

Server-Sent Events (SSEs)#

Under SSEs the client establishes a persistent and long-term connection with the server. The server uses this connection to send data to a client. If the client wants to send data to the server, it would require the use of another technology/protocol to do so.

  1. Client requests data from a server using regular HTTP.
  2. The requested webpage opens a connection to the server.
  3. The server sends the data to the client whenever there’s new information available.

SSEs are best when we need real-time traffic from the server to the client or if the server is generating data in a loop and will be sending multiple events to the client.

A High Performance Multi-Threaded LRU Cache

Introduction

A LRU Cache is a key-value based data container that is constrained by size and/or age, removing the least recently used objects first. This algorithm requires keeping track of the most recent time each object is accessed.

1:Most LRU Caching algorithms consist of two parts: a dictionary and a doubleLinkLlist. 

1:The dictionary guarantees quick access to your data.

2:In double linked list the node can remove itself without other reference. it takes constant time to add and remove nodes from the head or tail.

Alogorith 1: adding new items to the head, removing items from the tail, and moving any existing items to the head when referenced (touched). This algorithm is good for single threaded applications but becomes very slow in a multi-threaded environment.

In a multi-threaded app, every time the linked list is modified, it must be locked. This requires thread locks on insert, read, and delete operations, causing the slowness.

Alogorith 2:Another algorithm is to mark each item’s age with a timestamp (DateTime or incremented Integer) value when touched. When the cache becomes full, the list of items must be sorted and truncated. This keeps insert and read operations very fast because no locks are required, but makes delete painfully slow (normally O(N * log N), for sorting).

Available Library :https://github.com/bitfaster/BitFaster.Caching

 pseudo LRU designed for concurrent workloads – currently in use in a production system. Performance is very close to ConcurrentDictionary, ~10x faster than MemoryCache and hit rate is better than a conventional LRU. Full analysis provided in the github link below.

algorithm3:AgeBag’s

My implementation of LRUCache contains two distinct sections which are coded as subclasses. The LifespanMgr class tracks the item usage and determines which items to remove when the cache gets full/old. The Index class provides Dictionary key/value access to all objects stored in the cache. The Index has delegates to calculate the key of any item added to the cache, and to load an item by key/value from an external source.

Instead of storing item nodes in the Index, the Index holds WeakReference objects that point at the item nodes. By doing this, the Index does not prevent items from being garbage collected. This also allows old items to be removed from the AgeBag without having to remove the items from the index. Once a Node is removed from the LifespanMgr, it becomes eligible for garbage collection. The Node is not removed from the Indexes immediately. If an Index retrieves the node prior to garbage collection, it is reinserted into the current AgeBag’s node list. If it has already been garbage collected, a new object gets loaded. If the Index size exceeds twice the cache’s capacity, the Index is cleared and rebuilt.

ref :https://www.codeproject.com/Articles/23396/A-High-Performance-Multi-Threaded-LRU-Cache

Pseudo-LRU (PLRU)

For CPU caches with large associativity (generally >4 ways), the implementation cost of LRU becomes prohibitive. In many CPU caches, a scheme that almost always discards one of the least recently used items is sufficient, so many CPU designers choose a PLRU algorithm which only needs one bit per cache item to work. PLRU typically has a slightly worse miss ratio, has a slightly better latency, uses slightly less power than LRU and lower overheads compared to LRU.

Design a site like this with WordPress.com
Get started