❌

Normal view

There are new articles available, click to refresh the page.
Before yesterdayMain stream

Learning Notes #65 – Application Logs, Metrics, MDC

21 January 2025 at 05:45

I am big fan of logs. Would like to log everything. All the request, response of an API. But is it correct ? Though logs helped our team greatly during this new year, i want to know, is there a better approach to log things. That search made this blog. In this blog i jot down notes on logging. Lets log it.

Throughout this blog, i try to generalize things. Not biased to a particular language. But here and there you can see me biased towards Python. Also this is my opinion. Not a hard rule.

Which is a best logger ?

I’m not here to argue about which logger is the best, they all have their problems. But the worst one is usually the one you build yourself. Sure, existing loggers aren’t perfect, but trying to create your own is often a much bigger mistake.

1. Why Logging Matters

Logging provides visibility into your application’s behavior, helping to,

  • Diagnose and troubleshoot issues (This is most common usecase)
  • Monitor application health and performance (Metrics)
  • Meet compliance and auditing requirements (Audit Logs)
  • Enable debugging in production environments (we all do this.)

However, poorly designed logging strategies can lead to excessive log volumes, higher costs, and difficulty in pinpointing actionable insights.

2. Logging Best Practices

a. Use Structured Logs

Long story short, instead of unstructured plain text, use JSON or other structured formats. This makes parsing and querying easier, especially in log aggregation tools.


{
  "timestamp": "2025-01-20T12:34:56Z",
  "level": "INFO",
  "message": "User login successful",
  "userId": 12345,
  "sessionId": "abcde12345"
}

b. Leverage Logging Levels

Define and adhere to appropriate logging levels to avoid log bloat:

  • DEBUG: Detailed information for debugging.
  • INFO: General operational messages.
  • WARNING: Indications of potential issues.
  • ERROR: Application errors that require immediate attention.
  • CRITICAL: Severe errors leading to application failure.

c. Avoid Sensitive Data

Sanitize your logs to exclude sensitive information like passwords, PII, or API keys. Instead, mask or hash such data. Don’t add token even for testing.


d. Include Contextual Information

Incorporate metadata like request IDs, user IDs, or transaction IDs to trace specific events effectively.


3. Log Ingestion at Scale

As applications scale, log ingestion can become a bottleneck. Here’s how to manage it,

a. Centralized Logging

Stream logs to centralized systems like Elasticsearch, Logstash, Kibana (ELK), or cloud-native services like AWS CloudWatch, Azure Monitor, or Google Cloud Logging.

b. Optimize Log Volume

  • Log only necessary information.
  • Use log sampling to reduce verbosity in high-throughput systems.
  • Rotate logs to limit disk usage.

c. Use Asynchronous Logging

Asynchronous loggers improve application performance by delegating logging tasks to separate threads or processes. (Not Suitable all time. It has its own problems)

d. Method return values are usually important

If you have a log in the method and don’t include the return value of the method, you’re missing important information. Make an effort to include that at the expense of slightly less elegant looking code.

e. Include filename in error messages

Mention the path/to/file:line-number to pinpoint the location of the issue.

3. Logging Don’ts

a. Don’t Log Everything at the Same Level

Logging all messages at the INFO or DEBUG level creates noise and makes it difficult to identify critical issues.

b. Don’t Hardcode Log Messages

Avoid static, vague, or generic log messages. Use dynamic and descriptive messages that include relevant context.

# Bad Example
Error occurred.

# Good Example
Error occurred while processing payment for user_id=12345, transaction_id=abc-6789.

c. Don’t Log Sensitive or Regulated Data

Exposing personally identifiable information (PII), passwords, or other sensitive data in logs can lead to compliance violations (e.g., GDPR, HIPAA).

d. Don’t Ignore Log Rotation

Failing to implement log rotation can result in disk space exhaustion, especially in high traffic systems (Log Retention).

e. Don’t Overlook Log Correlation

Logs without request IDs, session IDs, or contextual metadata make it difficult to correlate related events.

f. Don’t Forget to Monitor Log Costs

Logging everything without considering storage and processing costs can lead to financial inefficiency in large-scale systems.

g. Keep the log message short

Long and verbose messages are a cost. The cost is in reading time and ingestion time.

h. Never use log message in loop

This might seem obvious, but just to be clear -> logging inside a loop, even if the log level isn’t visible by default, can still hurt performance. It’s best to avoid this whenever possible.

If you absolutely need to log something at a hidden level and decide to break this guideline, keep it short and straightforward.

i. Log item you already β€œhave”

We should avoid this,


logger.info("Reached X and value of method is {}", method());

Here, just for the logging purpose, we are calling the method() again. Even if the method is cheap. You’re effectively running the method regardless of the respective logging levels!

j. Dont log iterables

Even if it’s a small list. The concern is that the list might grow and β€œovercrowd” the log. Writing the content of the list to the log can balloon it up and slow processing noticeably. Also kills time in debugging.

k. Don’t Log What the Framework Logs for You

There are great things to log. E.g. the name of the current thread, the time, etc. But those are already written into the log by default almost everywhere. Don’t duplicate these efforts.

l.Don’t log Method Entry/Exit

Log only important events in the system. Entering or exiting a method isn’t an important event. E.g. if I have a method that enables feature X the log should be β€œFeature X enabled” and not β€œenable_feature_X entered”. I have done this a lot.

m. Dont fill the method

A complex method might include multiple points of failure, so it makes sense that we’d place logs in multiple points in the method so we can detect the failure along the way. Unfortunately, this leads to duplicate logging and verbosity.

Errors will typically map to error handling code which should be logged in generically. So all error conditions should already be covered.

This creates situations where we sometimes need to change the flow/behavior of the code, so logging will be more elegant.

n. Don’t use AOP logging

AOP (Aspect-Oriented Programming) logging allows you to automatically add logs at specific points in your application, such as when methods are entered or exited.

In Python, AOP-style logging can be implemented using decorators or middleware that inject logs into specific points, such as method entry and exit. While it might seem appealing for detailed tracing, the same problems apply as in other languages like Java.


import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def log_method_entry_exit(func):
    def wrapper(*args, **kwargs):
        logger.info(f"Entering: {func.__name__} with args={args} kwargs={kwargs}")
        result = func(*args, **kwargs)
        logger.info(f"Exiting: {func.__name__} with result={result}")
        return result
    return wrapper

# Example usage
@log_method_entry_exit
def example_function(x, y):
    return x + y

example_function(5, 3)

Why Avoid AOP Logging in Python

  1. Performance Impact:
    • Injecting logs into every method increases runtime overhead, especially if used extensively in large-scale systems.
    • In Python, where function calls already add some overhead, this can significantly affect performance.
  2. Log Verbosity:
    • If this decorator is applied to every function or method in a system, it produces an enormous amount of log data.
    • Debugging becomes harder because the meaningful logs are lost in the noise of entry/exit logs.
  3. Limited Usefulness:
    • During local development, tools like Python debuggers (pdb), profilers (cProfile, line_profiler), or tracing libraries like trace are far more effective for inspecting function behavior and performance.
  4. CI Issues:
    • Enabling such verbose logging during CI test runs can make tracking test failures more difficult because the logs are flooded with entry/exit messages, obscuring the root cause of failures.

Use Python-specific tools like pdb, ipdb, or IDE-integrated debuggers to inspect code locally.

o. Dont Double log

It’s pretty common to log an error when we’re about to throw an error. However, since most error code is generic, it’s likely there’s a log in the generic error handling code.

4. Ensuring Scalability

To keep your logging system robust and scalable,

  • Monitor Log Storage: Set alerts for log storage thresholds.
  • Implement Compression: Compress log files to reduce storage costs.
  • Automate Archival and Deletion: Regularly archive old logs and purge obsolete data.
  • Benchmark Logging Overhead: Measure the performance impact of logging on your application.

5. Logging for Metrics

Below, is the list of items that i wish can be logged for metrics.

General API Metrics

  1. General API Metrics on HTTP methods, status codes, latency/duration, request size.
  2. Total requests per endpoint over time. Requests per minute/hour.
  3. Frequency and breakdown of 4XX and 5XX errors.
  4. User ID or API client making the request.

{
  "timestamp": "2025-01-20T12:34:56Z",
  "endpoint": "/projects",
  "method": "POST",
  "status_code": 201,
  "user_id": 12345,
  "request_size_bytes": 512,
  "response_size_bytes": 256,
  "duration_ms": 120
}

Business Specific Metrics

  1. Objects (session) creations: No. of projects created (daily/weekly)
  2. Average success/failure rate.
  3. Average time to create a session.
  4. Frequency of each action on top of session.

{
  "timestamp": "2025-01-20T12:35:00Z",
  "endpoint": "/projects/12345/actions",
  "action": "edit",
  "status_code": 200,
  "user_id": 12345,
  "duration_ms": 98
}

Performance Metrics

  1. Database query metrics on execution time, no. of queries per request.
  2. Third party service metrics on time spent, success/failure rates of external calls.

{
  "timestamp": "2025-01-20T12:37:15Z",
  "endpoint": "/projects/12345",
  "db_query_time_ms": 45,
  "external_api_time_ms": 80,
  "status_code": 200,
  "duration_ms": 130
}

Scalability Metrics

  1. Concurrency metrics on max request handled.
  2. Request queue times during load.
  3. System Metrics on CPU and Memory usage during request processing (this will be auto captured).

Usage Metrics

  1. Traffic analysis on peak usage times.
  2. Most/Least used endpoints.

6. Mapped Diagnostic Context (MDC)

MDC is the one, i longed for most. Also went into trouble by implementing without a middleware.

Mapped Diagnostic Context (MDC) is a feature provided by many logging frameworks, such as Logback, Log4j, and SLF4J. It allows developers to attach contextual information (key-value pairs) to the logging events, which can then be automatically included in log messages.

This context helps in differentiating and correlating log messages, especially in multi-threaded applications.

Why Use MDC?

  1. Enhanced Log Clarity: By adding contextual information like user IDs, session IDs, or transaction IDs, MDC enables logs to provide more meaningful insights.
  2. Easier Debugging: When logs contain thread-specific context, tracing the execution path of a specific transaction or user request becomes straightforward.
  3. Reduced Log Ambiguity: MDC ensures that logs from different threads or components do not get mixed up, avoiding confusion.

Common Use Cases

  1. Web Applications: Logging user sessions, request IDs, or IP addresses to trace the lifecycle of a request.
  2. Microservices: Propagating correlation IDs across services for distributed tracing.
  3. Background Tasks: Tracking specific jobs or tasks in asynchronous operations.

Limitations (Curated from other blogs. I havent tried yet )

  1. Thread Boundaries: MDC is thread-local, so its context does not automatically propagate across threads (e.g., in asynchronous executions). For such scenarios, you may need to manually propagate the MDC context.
  2. Overhead: Adding and managing MDC context introduces a small runtime overhead, especially in high-throughput systems.
  3. Configuration Dependency: Proper MDC usage often depends on correctly configuring the logging framework.


2025-01-21 14:22:15.123 INFO  [thread-1] [userId=12345, transactionId=abc123] Starting transaction
2025-01-21 14:22:16.456 DEBUG [thread-1] [userId=12345, transactionId=abc123] Processing request
2025-01-21 14:22:17.789 ERROR [thread-1] [userId=12345, transactionId=abc123] Error processing request: Invalid input
2025-01-21 14:22:18.012 INFO  [thread-1] [userId=12345, transactionId=abc123] Transaction completed

In Fastapi, we can implement this via a middleware,


import logging
import uuid
from fastapi import FastAPI, Request
from starlette.middleware.base import BaseHTTPMiddleware

# Configure the logger
logger = logging.getLogger("uvicorn")
logger.setLevel(logging.INFO)

# Create a custom formatter with MDC placeholders
class CustomFormatter(logging.Formatter):
    def format(self, record):
        record.user_id = getattr(record, "user_id", "unknown")
        record.transaction_id = getattr(record, "transaction_id", str(uuid.uuid4()))
        return super().format(record)

# Set the logging format with MDC keys
formatter = CustomFormatter(
    "%(asctime)s %(levelname)s [%(threadName)s] [userId=%(user_id)s, transactionId=%(transaction_id)s] %(message)s"
)

# Apply the formatter to the handler
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)

# FastAPI application
app = FastAPI()

# Custom Middleware to add MDC context
class RequestContextMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        # Add MDC info before handling the request
        user_id = request.headers.get("X-User-ID", "default-user")
        transaction_id = str(uuid.uuid4())
        logging.getLogger().info(f"Request started: {user_id}, {transaction_id}")

        # Add MDC info to log
        logging.getLogger().user_id = user_id
        logging.getLogger().transaction_id = transaction_id

        response = await call_next(request)

        # Optionally, log additional information when the response is done
        logging.getLogger().info(f"Request finished: {user_id}, {transaction_id}")

        return response

# Add custom middleware to the FastAPI app
app.add_middleware(RequestContextMiddleware)

@app.get("/")
async def read_root():
    logger.info("Handling the root endpoint.")
    return {"message": "Hello, World!"}

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    logger.info(f"Fetching item with ID {item_id}")
    return {"item_id": item_id}

Hope, you might have got a better idea on logging.

Learning Notes #63 – Change Data Capture. What does it do ?

19 January 2025 at 16:22

Few days back i came across a concept of CDC. Like a notifier of database events. Instead of polling, this enables event to be available in a queue, which can be consumed by many consumers. In this blog, i try to explain the concepts, types in a theoretical manner.

You run a library. Every day, books are borrowed, returned, or new books are added. What if you wanted to keep a live record of all these activities so you always know the exact state of your library?

This is essentially what Change Data Capture (CDC) does for your databases. It’s a way to track changes (like inserts, updates, or deletions) in your database tables and send them to another system, like a live dashboard or a backup system. (Might be a bad example. Don’t lose hope. Continue …)

CDC is widely used in modern technology to power,

  • Real-Time Analytics: Live dashboards that show sales, user activity, or system performance.
  • Data Synchronization: Keeping multiple databases or microservices in sync.
  • Event-Driven Architectures: Triggering notifications, workflows, or downstream processes based on database changes.
  • Data Pipelines: Streaming changes to data lakes or warehouses for further processing.
  • Backup and Recovery: Incremental backups by capturing changes instead of full data dumps.

It’s a critical part of tools like Debezium, Kafka, and cloud services such as AWS Database Migration Service (DMS) and Azure Data Factory. CDC enables companies to move towards real-time data-driven decision-making.

What is CDC?

CDC stands for Change Data Capture. It’s a technique that listens to a database and captures every change that happens in it. These changes can then be sent to other systems to,

  • Keep data in sync across multiple databases.
  • Power real-time analytics dashboards.
  • Trigger notifications for certain database events.
  • Process data streams in real time.

In short, CDC ensures your data is always up-to-date wherever it’s needed.

Why is CDC Useful?

Imagine you have an online store. Whenever someone,

  • Places an order,
  • Updates their shipping address, or
  • Cancels an order,

you need these changes to be reflected immediately across,

  • The shipping system.
  • The inventory system.
  • The email notification service.

Instead of having all these systems query the database (this is one of main reasons) constantly (which is slow and inefficient), CDC automatically streams these changes to the relevant systems.

This means,

  1. Real-Time Updates: Systems receive changes instantly.
  2. Improved Performance: Your database isn’t overloaded with repeated queries.
  3. Consistency: All systems stay in sync without manual intervention.

How Does CDC Work?

Note: I haven’t yet tried all these. But conceptually having a feeling.

CDC relies on tracking changes in your database. There are a few ways to do this,

1. Query-Based CDC

This method repeatedly checks the database for changes. For example:

  • Every 5 minutes, it queries the database: β€œWhat changed since my last check?”
  • Any new or modified data is identified and processed.

Drawbacks: This can miss changes if the timing isn’t right, and it’s not truly real-time (Long Polling).

2. Log-Based CDC

Most modern databases (like PostgreSQL or MySQL) keep logs of every operation. Log-based CDC listens to these logs and captures changes as they happen.

Advantages

  • It’s real-time.
  • It’s lightweight since it doesn’t query the database directly.

3. Trigger-Based CDC

In this method, the database uses triggers to log changes into a separate table. Whenever a change occurs, a trigger writes a record of it.

Advantages: Simple to set up.

Drawbacks: Can slow down the database if not carefully managed.

Tools That Make CDC Easy

Several tools simplify CDC implementation. Some popular ones are,

  1. Debezium: Open-source and widely used for log-based CDC with databases like PostgreSQL, MySQL, and MongoDB.
  2. Striim: A commercial tool for real-time data integration.
  3. AWS Database Migration Service (DMS): A cloud-based CDC service.
  4. StreamSets: Another tool for real-time data movement.

These tools integrate with databases, capture changes, and deliver them to systems like RabbitMQ, Kafka, or cloud storage.

To help visualize CDC, think of,

  • Social Media Feeds: When someone likes or comments on a post, you see the update instantly. This is CDC in action.
  • Bank Notifications: Whenever you make a transaction, your bank app updates instantly. Another example of CDC.

In upcoming blogs, will include Debezium implementation with CDC.

Learning Notes #56 – Push vs Pull Architecture

15 January 2025 at 16:16

Today, i learnt about push vs pull architecture, the choice between push and pull architectures can significantly influence system performance, scalability, and user experience. Both approaches have their unique advantages and trade-offs. Understanding these architectures and their ideal use cases can help developers and architects make informed decisions.

What is Push Architecture?

Push architecture is a communication pattern where the server actively sends data to clients as soon as it becomes available. This approach eliminates the need for clients to repeatedly request updates.

How it Works

  • The server maintains a connection with the client.
  • When new data is available, the server β€œpushes” it to the connected clients.
  • In a message queue context, producers send messages to a queue, and the queue actively delivers these messages to subscribed consumers without explicit requests.

Examples

  • Notifications in Mobile Apps: Users receive instant updates, such as chat messages or alerts.
  • Stock Price Updates: Financial platforms use push to provide real-time market data.
  • Message Queues with Push Delivery: Systems like RabbitMQ or Kafka configured to push messages to consumers.
  • Server-Sent Events (SSE) and WebSockets: These are common implementations of push.

Advantages

  • Low Latency: Clients receive updates instantly, improving responsiveness.
  • Reduced Redundancy: No need for clients to poll servers frequently, reducing bandwidth consumption.

Challenges

  • Complexity: Maintaining open connections, especially for many clients, can be resource-intensive.
  • Scalability: Requires robust infrastructure to handle large-scale deployments.

What is Pull Architecture?

Pull architecture involves clients actively requesting data from the server. This pattern is often used when real-time updates are not critical or predictable intervals suffice.

How it Works

  • The client periodically sends requests to the server.
  • The server responds with the requested data.
  • In a message queue context, consumers actively poll the queue to retrieve messages when ready.

Examples

  • Web Browsing: A browser sends HTTP requests to fetch pages and resources.
  • API Data Fetching: Applications periodically query APIs to update information.
  • Message Queues with Pull Delivery: Systems like SQS or Kafka where consumers poll for messages.
  • Polling: Regularly checking a server or queue for updates.

Advantages

  • Simpler Implementation: No need for persistent connections; standard HTTP requests or queue polling suffice.
  • Server Load Control: The server can limit the frequency of client requests to manage resources better.

Challenges

  • Latency: Updates are only received when the client requests them, which might lead to delays.
  • Increased Bandwidth: Frequent polling can waste resources if no new data is available.

AspectPush ArchitecturePull Architecture
LatencyLow – Real-time updatesHigher – Dependent on polling frequency
ComplexityHigher – Requires persistent connectionsLower – Simple request-response model
Bandwidth EfficiencyEfficient – Updates sent only when neededLess efficient – Redundant polling possible
ScalabilityChallenging – High client connection overheadEasier – Controlled client request intervals
Message Queue FlowMessages actively delivered to consumersConsumers poll the queue for messages
Use CasesReal-time applications (e.g., chat, live data)Non-critical updates (e.g., periodic reports)

Learning Notes #55 – API Keys and Tokens

14 January 2025 at 05:27

Tokens and API keys are foundational tools that ensure secure communication between systems. They enable authentication, authorization, and access control, facilitating secure data exchange.

What Are Tokens?

Tokens are digital objects that represent a specific set of permissions or claims. They are often used in authentication and authorization processes to verify a user’s identity or grant access to resources. Tokens can be time-bound and carry information like:

  1. User Identity: Information about the user or system initiating the request.
  2. Scope of Access: Details about what actions or resources the token permits.
  3. Validity Period: Start and expiry times for the token.

Common Types of Tokens:

  • JWT (JSON Web Tokens): Compact, URL-safe tokens containing a payload, signature, and header.
  • Opaque Tokens: Tokens without embedded information; they require validation against a server.
  • Refresh Tokens: Used to obtain a new access token when the current one expires.

What Are API Keys?

API keys are unique identifiers used to authenticate applications or systems accessing APIs. They are simple to use and act as a credential to allow systems to make authorized API calls.

Key Characteristics:

  • Static Credential: Unlike tokens, API keys do not typically expire unless explicitly revoked.
  • Simple to Use: They are easy to implement and often passed in headers or query parameters.
  • Application-Specific: Keys are tied to specific applications rather than user accounts.

Functionalities and Usage

Both tokens and API keys enable secure interaction between systems, but their application depends on the scenario

1. Authentication

  • Tokens: Often used for user authentication in web apps and APIs.
    • Example: A JWT issued after login is included in subsequent API requests to validate the user’s session.
  • API Keys: Authenticate applications rather than users.
    • Example: A weather app uses an API key to fetch data from a weather API.

2. Authorization

  • Tokens: Define user-specific permissions and roles.
    • Example: A token allows read-only access to specific resources for a particular user.
  • API Keys: Grant access to predefined resources for the application.
    • Example: An API key allows access to public datasets but restricts write operations.

3. Rate Limiting and Monitoring

Both tokens and API keys can be used to

  • Enforce usage limits.
  • Monitor and log API usage for analytics and security.

Considerations for Secure Implementation

1. For Tokens

  • Use HTTPS: Always transmit tokens over HTTPS to prevent interception.
  • Implement Expiry: Set reasonable expiry times to minimize risks.
  • Adopt Refresh Tokens: Allow users to obtain new tokens securely when access tokens expire.
  • Validate Signatures: For JWTs, validate the signature to ensure the token’s integrity.

2. For API Keys

  • Restrict IP Usage: Limit the key’s use to specific IPs or networks.
  • Set Permissions: Assign the minimum required permissions for the API key.
  • Regenerate Periodically: Refresh keys periodically to mitigate risks.
  • Monitor Usage: Track API key usage for anomalies and revoke compromised keys promptly.

3. For Both

  • Avoid Hardcoding: Never embed tokens or keys in source code. Use environment variables or secure vaults.
  • Audit and Rotate: Regularly audit and rotate keys and tokens to maintain security.
  • Educate Users: Ensure users and developers understand secure handling practices.

Learning Notes #40 – SAGA Pattern | Cloud Patterns

5 January 2025 at 17:08

Today, I learnt about SAGA Pattern, followed by Compensation Pattern, Orchestration Pattern, Choreography Pattern and Two Phase Commit. SAGA is a combination of all the above. In this blog, i jot down notes on SAGA, for my future self.

Modern software applications often require the coordination of multiple distributed services to perform complex business operations. In such systems, ensuring consistency and reliability can be challenging, especially when a failure occurs in one of the services. The SAGA design pattern offers a robust solution to manage distributed transactions while maintaining data consistency.

What is the SAGA Pattern?

The SAGA pattern is a distributed transaction management mechanism where a series of independent operations (or steps) are executed sequentially across multiple services. Each operation in the sequence has a corresponding compensating action to roll back changes if a failure occurs. This approach avoids the complexities of distributed transactions, such as two-phase commits, by breaking down the process into smaller, manageable units.

Key Characteristics

  1. Decentralized Control: Transactions are managed across services without a central coordinator.
  2. Compensating Transactions: Every operation has an undo or rollback mechanism.
  3. Asynchronous Communication: Services communicate asynchronously in most implementations, ensuring loose coupling.

Types of SAGA Patterns

There are two primary types of SAGA patterns:

1. Choreography-Based SAGA

  • In this approach, services communicate with each other directly to coordinate the workflow.
  • Each service knows which operation to trigger next after completing its own task.
  • If a failure occurs, each service initiates its compensating action to roll back changes.

Advantages:

  • Simple implementation.
  • No central coordinator required.

Disadvantages:

  • Difficult to manage and debug in complex workflows.
  • Tight coupling between services.
import pika

class RabbitMQHandler:
    def __init__(self, queue):
        self.connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
        self.channel = self.connection.channel()
        self.channel.queue_declare(queue=queue)
        self.queue = queue

    def publish(self, message):
        self.channel.basic_publish(exchange='', routing_key=self.queue, body=message)

    def consume(self, callback):
        self.channel.basic_consume(queue=self.queue, on_message_callback=callback, auto_ack=True)
        self.channel.start_consuming()

# Define services
class FlightService:
    def book_flight(self):
        print("Flight booked.")
        RabbitMQHandler('hotel_queue').publish("flight_booked")

class HotelService:
    def on_flight_booked(self, ch, method, properties, body):
        try:
            print("Hotel booked.")
            RabbitMQHandler('invoice_queue').publish("hotel_booked")
        except Exception:
            print("Failed to book hotel. Rolling back flight.")
            FlightService().cancel_flight()

    def cancel_flight(self):
        print("Flight booking canceled.")

# Setup RabbitMQ
flight_service = FlightService()
hotel_service = HotelService()

RabbitMQHandler('hotel_queue').consume(hotel_service.on_flight_booked)

# Trigger the workflow
flight_service.book_flight()

2. Orchestration-Based SAGA

  • A central orchestrator service manages the workflow and coordinates between the services.
  • The orchestrator determines the sequence of operations and handles compensating actions in case of failures.

Advantages:

  • Clear control and visibility of the workflow.
  • Easier to debug and manage.

Disadvantages:

  • The orchestrator can become a single point of failure.
  • More complex implementation.
import pika

class Orchestrator:
    def __init__(self):
        self.rabbitmq = RabbitMQHandler('orchestrator_queue')

    def execute_saga(self):
        try:
            self.reserve_inventory()
            self.process_payment()
            self.generate_invoice()
        except Exception as e:
            print(f"Error occurred: {e}. Initiating rollback.")
            self.compensate()

    def reserve_inventory(self):
        print("Inventory reserved.")
        self.rabbitmq.publish("inventory_reserved")

    def process_payment(self):
        print("Payment processed.")
        self.rabbitmq.publish("payment_processed")

    def generate_invoice(self):
        print("Invoice generated.")
        self.rabbitmq.publish("invoice_generated")

    def compensate(self):
        print("Rolling back invoice.")
        print("Rolling back payment.")
        print("Rolling back inventory.")

# Trigger the workflow
Orchestrator().execute_saga()

How SAGA Works

  1. Transaction Initiation: The first operation is executed by one of the services.
  2. Service Communication: Subsequent services execute their operations based on the outcome of the previous step.
  3. Failure Handling: If an operation fails, compensating transactions are triggered in reverse order to undo any changes.
  4. Completion: Once all operations are successfully executed, the transaction is considered complete.

Benefits of the SAGA Pattern

  1. Improved Resilience: Allows partial rollbacks in case of failure.
  2. Scalability: Suitable for microservices and distributed systems.
  3. Flexibility: Works well with event-driven architectures.
  4. No Global Locks: Unlike traditional transactions, SAGA does not require global locking of resources.

Challenges and Limitations

  1. Complexity in Rollbacks: Designing compensating transactions for every operation can be challenging.
  2. Data Consistency: Achieving eventual consistency may require additional effort.
  3. Debugging Issues: Debugging failures in a distributed environment can be cumbersome.
  4. Latency: Sequential execution may increase overall latency.

When to Use the SAGA Pattern

  • Distributed systems where global ACID transactions are infeasible.
  • Microservices architectures with independent services.
  • Applications requiring high resilience and eventual consistency.

Real-World Applications

  1. E-Commerce Platforms: Managing orders, payments, and inventory updates.
  2. Travel Booking Systems: Coordinating flight, hotel, and car rental reservations.
  3. Banking Systems: Handling distributed account updates and transfers.
  4. Healthcare: Coordinating appointment scheduling and insurance claims.

Learning Notes #25 – Valet Key Pattern | Cloud Patterns

1 January 2025 at 17:20

Today, I learnt about Valet Key Pattern, which helps clients to directly access the resources without the server using a token. In this blog, i jot down notes on valet key pattern for better understanding.

The Valet Key Pattern is a security design pattern used to provide limited access to a resource or service without exposing full access credentials or permissions. It is akin to a physical valet key for a car, which allows the valet to drive the car without accessing the trunk or glove box. This pattern is widely employed in distributed systems, cloud services, and API design to ensure secure and controlled resource sharing.

Why Use the Valet Key Pattern?

Modern systems often require sharing access to specific resources while minimizing security risks. For instance:

  • A mobile app needs to upload files to a storage bucket but shouldn’t manage the entire bucket.
  • A third-party service requires temporary access to a user’s resource, such as a document or media file.
  • A system needs to allow time-bound or operation-restricted access to sensitive data.

In these scenarios, the Valet Key Pattern provides a practical solution by issuing a scoped, temporary, and revocable token (valet key) that grants specific permissions.

Core Principles of the Valet Key Pattern

  1. Scoped Access: The valet key grants access only to specific resources or operations.
  2. Time-Limited: The access token is typically valid for a limited duration to minimize exposure.
  3. Revocable: The issuing entity can revoke the token if necessary.
  4. Minimal Permissions: Permissions are restricted to the least privilege required to perform the intended task.

How the Valet Key Pattern Works

1. Resource Owner Issues a Valet Key

The resource owner (or controlling entity) generates a token with limited permissions. This token is often a signed JSON Web Token (JWT) or a pre-signed URL in the case of cloud storage.

2. Token Delivery to the Client

The token is securely delivered to the client or third-party application requiring access. For instance, the token might be sent via HTTPS or embedded in an API response.

3. Client Uses the Valet Key

The client includes the token in subsequent requests to access the resource. The resource server validates the token, checks its permissions, and allows or denies the requested operation accordingly.

4. Expiry or Revocation

Once the token expires or is revoked, it becomes invalid, ensuring the client can no longer access the resource.

Examples of the Valet Key Pattern in Action

1. Cloud Storage (Pre-signed URLs)

Amazon S3, Google Cloud Storage, and Azure Blob Storage allow generating pre-signed URLs that enable temporary, scoped access to specific files. For example, a user can upload a file using a URL valid for 15 minutes without needing direct access credentials.

2. API Design

APIs often issue temporary access tokens for limited operations. OAuth 2.0 tokens, for instance, can be scoped to allow access to specific endpoints or resources.

3. Media Sharing Platforms

Platforms like YouTube or Dropbox use the Valet Key Pattern to provide limited access to files. A shareable link often embeds permissions and expiration details.

Implementation Steps

1. Define Permissions Scope

Identify the specific operations or resources the token should allow. Use the principle of least privilege to limit permissions.

2. Generate Secure Tokens

Create tokens with cryptographic signing to ensure authenticity. Include metadata such as:

  • Resource identifiers
  • Permissions
  • Expiry time
  • Issuer information

3. Validate Tokens

The resource server must validate incoming tokens by checking the signature, expiration, and permissions.

4. Monitor and Revoke

Maintain a mechanism to monitor token usage and revoke them if misuse is detected.

Best Practices

  1. Use HTTPS: Always transmit tokens over secure channels to prevent interception.
  2. Minimize Token Lifetime: Short-lived tokens reduce the risk of misuse.
  3. Implement Auditing: Log token usage for monitoring and troubleshooting.
  4. Employ Secure Signing: Use robust cryptographic algorithms to sign tokens and prevent tampering.

Challenges

  • Token Management: Requires robust infrastructure for token generation, validation, and revocation.
  • Revocation Delays: Invalidation mechanisms may not instantly propagate in distributed systems.

IRC – My Understanding V2.0

By: Sugirtha
21 November 2024 at 10:47

What is plaintext in my point of view:
Its simply text without any makeup or add-on, it is just an organic content. For example,

  • A handwritten grocery list what our mother used to give to our father
  • A To-Do List
  • An essay/composition writing in our school days

Why plaintext is important?
– The quality of the content only going to get score here: there is no marketing by giving some beautification or formats.
– Less storage
– Ideal for long term data storage because Cross-Platform Compatibility
– Universal Accessibility. Many s/w using plain text for configuration files (.ini, .conf, .json)
– Data interchange (.csv – interchange data into databases or spreadsheet application)
– Command line environments, even in cryptography.
– Batch Processing: Many batch processes use plain text files to define lists of actions or tasks that need to be executed in a batch mode, such as renaming files, converting data formats, or running programs.

So plain text is simple, powerful and something special we have no doubt about it.

What is IRC?
IRC – Internet Relay Chat is a plain text based real time communication System over the internet for one-on-one chat, group chat, online community – making it ideal for discussion.

It’s a popular network for free and open-source software (FOSS) projects and developers in olden days. Ex. many large projects (like Debian, Arch Linux, GNOME, and Python) discussion used. Nowadays also IRC is using by many communities.

Usage :
Mainly a discussion chat forum for open-source software developers, technology, and hobbyist communities.

Why IRC?
Already we have so many chat platforms which are very advanced and I could use multimedia also there: but this is very basic, right? So Why should I go for this?

Yes it is very basic, but the infrastructure of this IRC is not like other chat platforms. In my point of view the important differences are Privacy and No Ads.

Advantages over other Chat Platforms:

  • No Ads Or Popups: We are not distracted from other ads or popups because my information is not shared with any companies for tracking or targeted marketing.
  • Privacy: Many IRC networks do not require your email, mobile number or even registration. You can simply type your name or nick name, select a server and start chatting instantly. Chat Logs also be stored if required.
  • Open Source and Free: Server, Client – the entire networking model is free and open source. Anybody can install the IRC servers/clients and connect with the network.
  • Decentralized : As servers are decentralized, it could able to work even one server has some issues and it is down. Users can connect to different servers within the same network which is improving reliability and performance.
  • Low Latency: Its a free real time communication system with low latency which is very important for technical communities and time sensitive conversations.
  • Customization and Extensibility: Custom scripts can be written to enhance functionality and IRC supports automation through bots which can record chats, sending notification or moderating channels, etc.
  • Channel Control: Channel Operators (Group Admin) have fine control over the users like who can join, who can be kicked off.
  • Light Weight Tool: As its light weight no high end hardware required. IRC can be accessed from even older computers or even low powered devices like Rasberry Pi.
  • History and Logging: Some IRC Servers allow logging of chats through bots or in local storage.

Inventor
IRC is developed by Jarkko Oikarinen (Finland) in 1988.

Some IRC networks/Servers:
Libera.Chat(#ubuntu, #debian, #python, #opensource)
EFNet-Eris Free Network (#linux, #python, #hackers)
IRCnet(#linux, #chat, #help)
Undernet(#help, #anime, #music)
QuakeNet (#quake, #gamers, #techsupport)
DALnet- for both casual users and larger communities (#tech, #gaming, #music)

Some Clients-GUI
HexChat (Linux, macOS, Windows)
Pidgin (Linux, Windows)
KVIrc (Linux, Windows, macOS)

Some IRC Clients for CLI (Command Line Interface) :
WeeChat
Irssi

IRC Clients for Mobile :
Goguma
Colloquy (iOS)
LimeChat (iOS)
Quassel IRC (via Quassel Core) (Android)
AndroIRC (Android)

Directly on the Website – Libera WebClient – https://web.libera.chat/gamja/ You can click Join, then type the channel name (Group) (Ex. #kaniyam)

How to get Connected with IRC:
After installed the IRC client, open.
Add a new network (e.g., β€œLibera.Chat”).
Set the server to irc.libera.chat (or any of the alternate servers above).
Optionally, you can specify a port (default is 6667 for non-SSL, 6697 for SSL).
Join a channel like #ubuntu, #python, or #freenode-migrants once you’re connected.

Popular channels to join on libera chat:
#ubuntu, #debian, #python, #opensource, #kaniyam

Local Logs:
Logs are typically saved in plain text and can be stored locally, allowing you to review past conversations.
How to get local logs from our System (IRC libera.chat Server)
folders – /home//.local/share/weechat/logs/ From Web-IRCBot History:
https://ircbot.comm-central.org:8080

References:
https://kaniyam.com/what-is-irc-an-introduction/
https://www.youtube.com/watch?v=CGurYNb0BM8

Our daily meetings :
You can install IRC client, with the help of above link, can join.
Timings : IST 8pm-9pm
Server : libera.chat
Channel : #kaniyam

ALL ARE WELCOME TO JOIN, DISCUSS and GROW

Different Database Models

23 August 2024 at 01:50

Database models define the structure, relationships, and operations that can be performed on a database. Different database models are used based on the specific needs of an application or organization. Here are the most common types of database models:

1. Hierarchical Database Model

  • Structure: Data is organized in a tree-like structure with a single root, where each record has a single parent but can have multiple children.
  • Usage: Best for applications with a clear hierarchical relationship, like organizational structures or file systems.
  • Example: IBM’s Information Management System (IMS).
  • Advantages: Fast access to data through parent-child relationships.
  • Disadvantages: Rigid structure; difficult to reorganize or restructure.

2. Network Database Model

  • Structure: Data is organized in a graph structure, where each record can have multiple parent and child records, forming a network of relationships.
  • Usage: Useful for complex relationships, such as in telecommunications or transportation networks.
  • Example: Integrated Data Store (IDS).
  • Advantages: Flexible representation of complex relationships.
  • Disadvantages: Complex design and navigation; can be difficult to maintain.

3. Relational Database Model

  • Structure: Data is organized into tables (relations) where each table consists of rows (records) and columns (fields). Relationships between tables are managed through keys.
  • Usage: Widely used in various applications, including finance, retail, and enterprise software.
  • Example: MySQL, PostgreSQL, Oracle Database, Microsoft SQL Server.
  • Advantages: Simplicity, data integrity, flexibility in querying through SQL.
  • Disadvantages: Can be slower for very large datasets or highly complex queries.

4. Object-Oriented Database Model

  • Structure: Data is stored as objects, similar to objects in object-oriented programming. Each object contains both data and methods for processing the data.
  • Usage: Suitable for applications that require the modeling of complex data and relationships, such as CAD, CAM, and multimedia databases.
  • Example: db4o, ObjectDB.
  • Advantages: Seamless integration with object-oriented programming languages, reusability of objects.
  • Disadvantages: Complexity, not as widely adopted as relational databases.

5. Document-Oriented Database Model

  • Structure: Data is stored in document collections, with each document being a self-contained piece of data often in JSON, BSON, or XML format.
  • Usage: Ideal for content management systems, real-time analytics, and big data applications.
  • Example: MongoDB, CouchDB.
  • Advantages: Flexible schema design, scalability, ease of storing hierarchical data.
  • Disadvantages: May require denormalization, leading to potential data redundancy.

6. Key-Value Database Model

  • Structure: Data is stored as key-value pairs, where each key is unique, and the value can be a string, number, or more complex data structure.
  • Usage: Best for applications requiring fast access to simple data, such as caching, session management, and real-time analytics.
  • Example: Redis, DynamoDB, Riak.
  • Advantages: High performance, simplicity, scalability.
  • Disadvantages: Limited querying capabilities, lack of complex relationships.

7. Column-Family Database Model

  • Structure: Data is stored in columns rather than rows, with each column family containing a set of columns that are logically related.
  • Usage: Suitable for distributed databases, handling large volumes of data across multiple servers.
  • Example: Apache Cassandra, HBase.
  • Advantages: High write and read performance, efficient storage of sparse data.
  • Disadvantages: Complexity in design and maintenance, not as flexible for ad-hoc queries.

8. Graph Database Model

  • Structure: Data is stored as nodes (entities) and edges (relationships) forming a graph. Each node represents an object, and edges represent the relationships between objects.
  • Usage: Ideal for social networks, recommendation engines, fraud detection, and any scenario where relationships between entities are crucial.
  • Example: Neo4j, Amazon Neptune.
  • Advantages: Efficient traversal and querying of complex relationships, flexible schema.
  • Disadvantages: Not as efficient for operations on large sets of unrelated data.

9. Multimodel Database

  • Structure: Supports multiple data models (e.g., relational, document, graph) within a single database engine.
  • Usage: Useful for applications that require different types of data storage and querying mechanisms.
  • Example: ArangoDB, Microsoft Azure Cosmos DB.
  • Advantages: Flexibility, ability to handle diverse data requirements within a single system.
  • Disadvantages: Complexity in management and optimization.

10. Time-Series Database Model

  • Structure: Specifically designed to handle time-series data, where each record is associated with a timestamp.
  • Usage: Best for applications like monitoring, logging, and real-time analytics where data changes over time.
  • Example: InfluxDB, TimescaleDB.
  • Advantages: Optimized for handling and querying large volumes of time-stamped data.
  • Disadvantages: Limited use cases outside of time-series data.

11. NoSQL Database Model

  • Structure: An umbrella term for various non-relational database models, including key-value, document, column-family, and graph databases.
  • Usage: Ideal for handling unstructured or semi-structured data, and scenarios requiring high scalability and flexibility.
  • Example: MongoDB, Cassandra, Couchbase, Neo4j.
  • Advantages: Flexibility, scalability, high performance for specific use cases.
  • Disadvantages: Lack of standardization, potential data consistency challenges.

Summary

Each database model serves different purposes, and the choice of model depends on the specific requirements of the application, such as data structure, relationships, performance needs, and scalability. While relational databases are still the most widely used, NoSQL and specialized databases have become increasingly important for handling diverse data types and large-scale applications.

❌
❌