Observability Done Right: Best Practices and Anti-Patterns for Effective System Monitoring

Image
  WHAT Observability is a concept that refers to the ability to gain insights into the behavior and performance of complex systems. In the context of software engineering, observability involves the collection, analysis, and visualization of data from software applications, infrastructure, and other components of a system. In the animal kingdom, observability plays a critical role in survival, allowing animals to monitor their surroundings, detect threats, and find food. Dolphins use echolocation to observe their surroundings. They emit high-frequency sounds that bounce off objects, allowing them to create a 3D map of their environment. Thanks for reading Knowledge Cafe! Subscribe for free to receive new posts and support my work. Subscribed WHY In today's era, architectures are becoming increasingly large, complex, and fast-paced due to the faster development and deployment of software by distributed teams with the help of DevOps, continuous delivery, and agile development methodo...

Semaphores - Java Concurrency



Semaphores are an often misunderstood and under used tool for restricting access to resources. They are ignored for other ways of controlling access to resources. But semaphores give us a tool set that goes beyond what normal synchronization and other tools can give us.

The simplest way to describe a semaphore is a mechanism to allows n units to be acquired to access particular resource. A semaphore is a synchronization object that controls access by multiple processes to a common resource in a parallel programming environment. Semaphores are widely used to control access to files and shared memory.

Semaphore is a technique used to control access to common resource (like database connection,  file operation,  ports etc) for competing multiple processes. Semaphore maintains a counter which keeps track of the number of resources available. When a process requests access to resource, semaphore checks the variable count and if it is less than total count then grants access and subsequently reduces the available count.

Semaphore is just a gatekeeper guarding the resources. If available grants access and otherwise asks the processes to wait.

  • When the resource count is more than 1, then this is called counting semaphore.

  • If resource count is only one and the state value is restricted to on/off, then it is called binary semaphore.
Very useful Book to learn about concurrency...


This concurrency utility can be very useful to implement producer consumer design pattern or implement bounded pool or resources like Thread Pool, DB Connection pool etc. java.util.Semaphore class represent a Counting semaphore which is initialized with number of permits. Semaphore provides two main method acquire() and release() for getting permits and releasing permits.

Counting Semaphore Example:


Suppose we got requirement to have resouce which can be accessed by 3 resource at a time while other request has to wait for resource to be free for access.  Below is the code to implement such behaviour.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
MyThread Class:
package semaphorepool;

class MyThread extends Thread {
 MyLimitedResource resource;

 public MyThread(String s, MyLimitedResource r) {
  super(s);
  resource=r;
 }
 public void run() {
  resource.printResourceName();
 }
}

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
MyLimitedResource class
package semaphorepool;

import java.util.Calendar;

public class MyLimitedResource {

 ConnectionLimiter con;
 
 public MyLimitedResource(ConnectionLimiter connectionLimiter){
  con=connectionLimiter;
 }
 
 public void printResourceName(){
  try{
  String resourceHeldBy= Thread.currentThread().getName();
  con.acquire();
  Calendar cal = Calendar.getInstance();
     cal.getTime();
  System.out.println(cal.getTime()+ "- Currently Resource Held by "+ resourceHeldBy);
  Thread.sleep(10000);
  con.release();
  System.out.println(cal.getTime()+ "- Resource Released by "+ resourceHeldBy);
  }catch (InterruptedException i){
   i.printStackTrace();
  }
 }
}

ConnectionLimiter Class
package semaphorepool;

import java.util.concurrent.Semaphore;

public class ConnectionLimiter {
 private final Semaphore semaphore;
 public ConnectionLimiter(int maxConcurrentRequests) {
  semaphore = new Semaphore(maxConcurrentRequests);
 }
 public void acquire() throws InterruptedException {
  semaphore.acquire(); 
 }

 public void release() {
  try {
   System.out.println("Clean up before releasing resource");
  } finally {
   semaphore.release();
  }
 }
}

Semaphore Test class:
package semaphorepool;

public class SemaphoreTest {

 public static void main(String[] args) {
  ConnectionLimiter limiter = new ConnectionLimiter(3);
  final MyLimitedResource resource = new MyLimitedResource(limiter);
  for (int i = 0; i < 10; i++) {
   new MyThread("Thread-" + i, resource).start();
  }
 }
}

Output (PS: This may vary as per JVM version and OS settings)
Sun Sep 14 13:17:37 CEST 2014- Currently Resource Held by Thread-2
Sun Sep 14 13:17:37 CEST 2014- Currently Resource Held by Thread-0
Sun Sep 14 13:17:37 CEST 2014- Currently Resource Held by Thread-1
Clean up before releasing resource
Sun Sep 14 13:17:37 CEST 2014- Resource Released by Thread-2
Sun Sep 14 13:17:47 CEST 2014- Currently Resource Held by Thread-4
Clean up before releasing resource
Sun Sep 14 13:17:37 CEST 2014- Resource Released by Thread-0
Clean up before releasing resource
Sun Sep 14 13:17:37 CEST 2014- Resource Released by Thread-1
Sun Sep 14 13:17:47 CEST 2014- Currently Resource Held by Thread-6
Sun Sep 14 13:17:47 CEST 2014- Currently Resource Held by Thread-5
Clean up before releasing resource
Sun Sep 14 13:17:57 CEST 2014- Currently Resource Held by Thread-8
Sun Sep 14 13:17:47 CEST 2014- Resource Released by Thread-4
Clean up before releasing resource
Clean up before releasing resource
Sun Sep 14 13:17:57 CEST 2014- Currently Resource Held by Thread-3
Sun Sep 14 13:17:47 CEST 2014- Resource Released by Thread-5
Sun Sep 14 13:17:47 CEST 2014- Resource Released by Thread-6
Sun Sep 14 13:17:57 CEST 2014- Currently Resource Held by Thread-9
Clean up before releasing resource
Sun Sep 14 13:17:57 CEST 2014- Resource Released by Thread-8
Sun Sep 14 13:18:07 CEST 2014- Currently Resource Held by Thread-7
Clean up before releasing resource
Sun Sep 14 13:17:57 CEST 2014- Resource Released by Thread-3
Clean up before releasing resource
Sun Sep 14 13:17:57 CEST 2014- Resource Released by Thread-9
Clean up before releasing resource
Sun Sep 14 13:18:07 CEST 2014- Resource Released by Thread-7</pre>

Dangers/Pitfalls


As with most methods of locking or synchronization, there are some potential issues.

The number one thing to remember is, always release what you acquire. Always write release code in finally so irrespective of exception acquired resource will be released. (We have used try..finally in our code above)

Reference:

http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/Semaphore.html

Popular posts from this blog

Chain of responsibility using Spring @Autowired List

Iterate Through a HashMap

Under the Hood: Understanding the Gossip Protocol in Apache Cassandra