Measure Execution Time with timeit() in Python

https://blog.finxter.com/wp-content/uploads/2023/08/pexels-photo-1095601.jpeg

5/5 – (1 vote)

Understanding Timeit in Python

The timeit module is a tool in the Python standard library, designed to measure the execution time of small code snippets. It makes it simple for developers to analyze the performance of their code, allowing them to find areas for optimization.

⏱ The timeit module averages out various factors that affect the execution time, such as the system load and fluctuations in CPU performance. By running the code snippet multiple times and calculating an average execution time, it provides a more reliable measure of your code’s performance.

To get started using timeit, simply import the module and use the timeit() method. This method accepts a code snippet as a string and measures its execution time. Optionally, you can also pass the number parameter to specify how many times the code snippet should be executed.

Here’s a quick example:

import timeit

code_snippet = '''
def example_function():
    return sum(range(10))

example_function()
'''

execution_time = timeit.timeit(code_snippet, number=1000)
print(f"Execution time: {execution_time:.6f} seconds")

Sometimes, you might want to evaluate a code snippet that requires additional imports or setup code. For this purpose, the timeit() method accepts a setup parameter where you can provide any necessary preparation code.

For instance, if we adjust the previous example to include a required import:

import timeit

code_snippet = '''
def example_function():
    return sum(range(10))

example_function()
'''

setup_code = "import math"

execution_time = timeit.timeit(code_snippet, setup=setup_code, number=1000)
print(f"Execution time: {execution_time:.6f} seconds")

Keep in mind that timeit is primarily intended for small code snippets and may not be suitable for benchmarking large-scale applications.

Measuring Execution Time

The primary method of measuring execution time with timeit is the timeit() function. This method runs the provided code repeatedly and returns the total time taken. By default, it repeats the code one million times! Be careful when measuring time-consuming code, as it may take a considerable duration.

import timeit

code_to_test = '''
example_function()
'''

elapsed_time = timeit.timeit(code_to_test, number=1000)
print(f'Elapsed time: {elapsed_time} seconds')

When using the timeit() method, the setup time is excluded from execution time. This way, the measurement is more accurate and focuses on the evaluated code’s performance, without including the time taken to configure the testing environment.

Another useful method in the timeit module is repeat(), which calls the timeit() function multiple times and returns a list of results.

results = timeit.repeat(code_to_test, repeat=3, number=1000)
averaged_result = sum(results) / len(results)
print(f'Average elapsed time: {averaged_result} seconds')

Sometimes it’s necessary to compare the execution speeds of different code snippets to identify the most efficient implementation. With the time.time() function, measuring the execution time of multiple code sections is simplified.

import time

start_time = time.time()
first_example_function()
end_time = time.time()

elapsed_time_1 = end_time - start_time

start_time = time.time()
second_example_function()
end_time = time.time()

elapsed_time_2 = end_time - start_time

print(f'First function elapsed time: {elapsed_time_1} seconds')
print(f'Second function elapsed time: {elapsed_time_2} seconds')

In conclusion, using the timeit module and the time.time() function allows you to accurately measure and compare execution times in Python.

The Timeit Module

To start using the timeit module, simply import it:

import timeit

The core method in the timeit module is the timeit() method used to run a specific code snippet a given number of times, returning the total time taken.

For example, suppose we want to measure the time it takes to square a list of numbers using a list comprehension:

import timeit

code_to_test = """
squared_numbers = [x**2 for x in range(10)]
"""

elapsed_time = timeit.timeit(code_to_test, number=1000)
print("Time taken:", elapsed_time)

If you are using Jupyter Notebook, you can take advantage of the %timeit magic function to conveniently measure the execution time of a single line of code:

%timeit squared_numbers = [x**2 for x in range(10)]

In addition to the timeit() method, the timeit module provides repeat() and autorange() methods.

  • The repeat() method allows you to run the timeit() method multiple times and returns a list of execution times, while
  • the autorange() method automatically determines the number of loops needed for a stable measurement.

Here’s an example using the repeat() method:

import timeit

code_to_test = """
squared_numbers = [x**2 for x in range(10)]
"""

elapsed_times = timeit.repeat(code_to_test, number=1000, repeat=5)
print("Time taken for each run:", elapsed_times)

Using Timeit Function

To measure the execution time of a function, you can use the timeit.timeit() method. This method accepts two main arguments: the stmt and setup. The stmt is a string representing the code snippet that you want to time, while the setup is an optional string that can contain any necessary imports and setup steps. Both default to 'pass' if not provided.

Let’s say you have a function called square() that calculates the square of a given number:

def square(x):
    return x ** 2

To measure the execution time of square() using timeit, you can do the following:

results = timeit.timeit('square(10)', 'from __main__ import square', number=1000)

Here, we’re asking timeit to execute the square(10) function 1000 times and return the total execution time in seconds. You can adjust the number parameter to run the function for a different number of iterations.

Another way to use timeit, especially for testing a callable function, is to use the timeit.Timer class. You can pass the callable function directly as the stmt parameter without the need for a setup string:

timer = timeit.Timer(square, args=(10,))
results = timer.timeit(number=1000)

Now you have your execution time in the results variable, which you can analyze and compare with other functions’ performance.

Examples and Snippets

The simplest way to use timeit.timeit() is by providing a statement as a string, which is the code snippet we want to measure the execution time for.

Here’s an example:

import timeit

code_snippet = "sum(range(100))"
elapsed_time = timeit.timeit(code_snippet, number=1000)
print(f"Execution time: {elapsed_time:.6f} seconds")

In the example above, we measure the time it takes to execute sum(range(100)) 1000 times. The number parameter controls how many repetitions of the code snippet are performed. By default, number=1000000, but you can set it to any value you find suitable.

For more complex code snippets with multiple lines, we can use triple quotes to define a multiline string:

Python Timeit Functions

The timeit module in Python allows you to accurately measure the execution time of small code snippets. It provides two essential functions: timeit.timeit() and timeit.repeat().

⏱ The timeit.timeit() function measures the execution time of a given statement. You can pass the stmt argument as a string containing the code snippet you want to time. By default, timeit.timeit() will execute the statement 1,000,000 times and return the average time taken to run it.

However, you can adjust the number parameter to specify a different number of iterations.

For example:

import timeit

code_to_test = "sum(range(100))"

execution_time = timeit.timeit(code_to_test, number=10000)
print(execution_time)

⏱ The timeit.repeat() function is a convenient way to call timeit.timeit() multiple times. It returns a list of timings for each repetition, allowing you to analyze the results more thoroughly. You can use the repeat parameter to specify the number of repetitions.

Here’s an example:

import timeit

code_to_test = "sum(range(100))"

execution_times = timeit.repeat(code_to_test, number=10000, repeat=5)
print(execution_times)

In some cases, you might need to include additional setup code to prepare your test environment. You can do this using the setup parameter, which allows you to define the necessary setup code as a string. The execution time of the setup code will not be included in the overall timed execution.

import timeit

my_code = '''
def example_function():
    return sum(range(100))

example_function()
'''

setup_code = "from __main__ import example_function"

result = timeit.timeit(my_code, setup=setup_code, number=1000)
print(result)

Measuring Execution Time of Code Blocks

The timeit module provides a straightforward interface for measuring the execution time of small code snippets. You can use this module to measure the time taken by a particular code block in your program.

Here’s a brief example:

import timeit

def some_function():
    # Your code block here

time_taken = timeit.timeit(some_function, number=1)
print(f"Time taken: {time_taken} seconds")

In this example, the timeit.timeit() function measures the time taken to execute the some_function function. The number parameter specifies the number of times the function will be executed, which is set to 1 in this case.

For more accurate results, you can use the timeit.repeat() function, which measures the time taken by the code block execution for multiple iterations.

Here’s an example:

import timeit

def some_function():
    # Your code block here

repeat_count = 5
time_taken = timeit.repeat(some_function, number=1, repeat=repeat_count)
average_time = sum(time_taken) / repeat_count
print(f"Average time taken: {average_time} seconds")

In this example, the some_function function is executed five times, and the average execution time is calculated.

Besides measuring time for standalone functions, you can also measure the time taken by individual code blocks inside a function. Here’s an example:

import timeit

def some_function():
    # Some code here

    start_time = timeit.default_timer()
    # Code block to be measured
    end_time = timeit.default_timer()

    print(f"Time taken for code block: {end_time - start_time} seconds")

In this example, the timeit.default_timer() function captures the start and end times of the specified code block.

Using Timeit with Jupyter Notebook

Using Timeit with Jupyter Notebook

Jupyter Notebook provides an excellent environment for running and testing Python code. To measure the execution time of your code snippets in Jupyter Notebook, you can use the %timeit and %%timeit magic commands, which are built into the IPython kernel.

⏱ The %timeit command is used to measure the execution time of a single line of code. When using it, simply prefix your line of code with %timeit.

For example:

%timeit sum(range(100))

This command will run the code multiple times and provide you with detailed statistics like the average time and standard deviation.

⏱ To measure the execution time of a code block spanning multiple lines, you can use the %%timeit magic command. Place this command at the beginning of a cell in Jupyter Notebook, and it will measure the execution time for the entire cell.

For example:

%%timeit
total = 0
for i in range(100):
    total += i

Managing Garbage Collection and Overhead

When using timeit in Python to measure code execution time, it is essential to be aware of the impact of garbage collection and overhead.

???? Garbage collection is the process of automatically freeing up memory occupied by objects that are no longer in use. This can potentially impact the accuracy of timeit measurements if left unmanaged.

By default, timeit disables garbage collection to avoid interference with the elapsed time calculations. However, you may want to include garbage collection in your measurements if it is a significant part of your code’s execution, or if you want to minimize the overhead and get more realistic results.

To include garbage collection in timeit executions, you can use the gc.enable() function from the gc module and customize your timeit setup.

Here’s an example:

import timeit
import gc

mysetup = "import gc; gc.enable()"
mycode = """
def my_function():
    # Your code here
    pass
my_function()
"""

elapsed_time = timeit.timeit(setup=mysetup, stmt=mycode, number=1000)
print(elapsed_time)

Keep in mind that including garbage collection will likely increase the measured execution time. Manage this overhead by balancing the need for accurate measurements with the need to see the impact of garbage collection on your code.

Additionally, you can use the timeit.repeat() and timeit.autorange() methods to measure execution time of your code snippets multiple times, which can help you capture the variability introduced by garbage collection and other factors.

Choosing the Best Timer for Performance Measurements

Measuring the execution time of your Python code is essential for optimization, and the timeit module offers multiple ways to achieve this. This section will focus on selecting the best timer for measuring performance.

When using the timeit module, it is crucial to choose the right timer function. Different functions may provide various levels of accuracy and be suitable for different use cases. The two main timer functions are time.process_time() and time.perf_counter().

time.process_time() measures the total CPU time used by your code, excluding any time spent during the sleep or wait state. This is useful for focusing on the computational efficiency of your code. This function is platform-independent and has a higher resolution on some operating systems.

Here is an example code snippet:

import time
import timeit

start = time.process_time()

# Your code here

end = time.process_time()
elapsed = end - start
print(f"Execution time: {elapsed} seconds")

On the other hand, time.perf_counter() measures the total elapsed time, including sleep or wait states. This function provides a more accurate measurement of the total time required by your code to execute. This can help in understanding the real-world performance of your code.

Here’s an example using time.perf_counter():

import time
import timeit

start = time.perf_counter()

# Your code here

end = time.perf_counter()
elapsed = end - start
print(f"Execution time: {elapsed} seconds")

In addition to measuring execution time directly, you can also calculate the time difference using the datetime module. This module provides a more human-readable representation of time data.

Here’s an example code snippet that calculates the time difference using datetime:

from datetime import datetime

start = datetime.now()

# Your code here

end = datetime.now()
elapsed = end - start
print(f"Execution time: {elapsed}")

Frequently Asked Questions

How to measure function execution time using timeit?

To measure the execution time of a function using the timeit module, you can use the timeit.timeit() method. First, import the timeit module, and then create a function you want to measure. You can call the timeit.timeit() method with the function’s code and the number of executions as arguments.

For example:

import timeit

def my_function():
    # Your code here

execution_time = timeit.timeit(my_function, number=1000)
print("Execution time:", execution_time)

What is the proper way to use the timeit module in Python?

The proper way to use the timeit module is by following these steps:

  1. Import the timeit module.
  2. Define the code or function to be timed.
  3. Use the timeit.timeit() method to measure the execution time, and optionally specify the number of times the code should be executed.
  4. Print or store the results for further analysis.

How to time Python functions with arguments using timeit?

To time a Python function that takes arguments using timeit, you can use a lambda function or functools.partial(). For example:

import timeit
from functools import partial

def my_function(arg1, arg2):
    # Your code here

# Using a lambda function
time_with_lambda = timeit.timeit(lambda: my_function("arg1", "arg2"), number=1000)

# Using functools.partial()
my_function_partial = partial(my_function, "arg1", "arg2")
time_with_partial = timeit.timeit(my_function_partial, number=1000)

What are the differences between timeit and time modules?

The timeit module is specifically designed for measuring small code snippets’ execution time, while the time module is more generic for working with time-related functions. The timeit module provides more accurate and consistent results for timing code execution, as it disables the garbage collector and uses an internal loop, reducing the impact of external factors.

How to use timeit in a Jupyter Notebook?

In a Jupyter Notebook, use the %%timeit cell magic command to measure the execution time of a code cell:

%%timeit
# Your code here

This will run the code multiple times and provide the average execution time and standard deviation.

What is the best practice for measuring execution time with timeit.repeat()?

The timeit.repeat() method is useful when you want to measure the execution time multiple times and then analyze the results. The best practice is to specify the number of repeats, the number of loops per repeat, and analyze the results to find the fastest, slowest, or average time. For example:

import timeit

def my_function():
    # Your code here

repeat_results = timeit.repeat(my_function, number=1000, repeat=5)
fastest_time = min(repeat_results)
slowest_time = max(repeat_results)
average_time = sum(repeat_results) / len(repeat_results)

Using timeit.repeat() allows you to better understand the function’s performance in different situations and analyze the variability in execution time.

???? Recommended: How to Determine Script Execution Time in Python?

The post Measure Execution Time with timeit() in Python appeared first on Be on the Right Side of Change.

Be on the Right Side of Change

How to Cast Harry Potter Spells on Your iPhone With Siri

https://static1.makeuseofimages.com/wordpress/wp-content/uploads/2023/08/a-wizard-with-a-wand-casting-spell-next-to-harry-potter-books.jpg

If you love Harry Potter and use an iPhone, you’re in luck! Using Siri, your iPhone can become your very own wand. Apple has integrated certain Harry Potter spell commands into iPhones. And with Siri, you can cast your own spells to show off to your friends.

With that said, if you’re ready for a crash course in spell casting, here are some built-in Harry Potter spells you can use on your iPhone.

Built-In Harry Potter Spells You Can Use

As stated earlier, your iPhone has built-in Harry Potter spells (voice commands) that you can activate with Siri. But before you can start casting any spells, you’ll need to enable Siri on your iPhone if you haven’t already.

Once Siri is enabled, you are ready to start casting spells. At the moment, Apple has programmed three Harry Potter spells into its devices: Lumos, Nox, and Accio. Here’s what they do:

Lumos

In Harry Potter, Lumos is used to cast light in a dark room, and it works similarly on your iPhone. When cast, the spell turns on your iPhone’s flashlight. To activate Lumos, say, “Hey Siri, Lumos.” Lumos is pronounced “loo-mows.”

Nox

Nox is the counter spell to Lumos. So saying, “Hey Siri, Nox” will turn off your iPhone’s flashlight when you’re done using it. And if you weren’t aware already, Nox is pronounced “knocks.”

Accio

In Harry Potter, Accio is a summoning charm to draw objects toward the caster. In the case of your iPhone, Accio is used to open an app on your phone.

So saying, “Hey Siri, Accio [app name]” will open the app in question. For instance, saying “Hey Siri, Accio Spotify” will open the Spotify app on your iPhone. Accio is pronounced “Ack-ee-oh.”

How to Create Your Own Harry Potter Siri Spells

Besides those three spells, Apple lets you create additional spells for Siri commands. To do this, you must manually set up the spells and their corresponding actions using the Shortcuts app on your iPhone.

For example, let’s set up the spell “Lumos Maxima.” This spell turns on your iPhone’s flashlight at the highest brightness level. Here’s what you need to do:

  1. Open the Shortcuts app, head to the Shortcuts section, and tap the plus (+) icon at the top-right corner.
  2. Then, tap New Shortcut at the top to rename it to the name of your spell. In this case, you’ll want to rename it to Lumos Maxima.
  3. Tap Add Action.
  4. From the menu that appears, tap Set Flashlight.
  5. Now, tap the blue arrow (next to On) and drag the slider all the way to the right.
  6. Tap the Play button at the bottom of the screen to test it out. Then, tap Done.

Now, say, “Hey Siri, Lumos Maxima!” to see if the spell command worked as intended.

Turn Your iPhone Into a Wand With Siri

Thanks to Siri, you have your very own wand at your fingertips. Now, you can perform simple tasks at any instance with these spells. Like Harry Potter, what you choose to do with this power is up to you.

You can try out different spells, such as Silencio to activate Do Not Disturb or Muffliato and Sonorus to control your iPhone’s volume.

MakeUseOf

How to Generate and Read Barcodes With Python

https://static1.makeuseofimages.com/wordpress/wp-content/uploads/2022/06/Barcode.jpg

When you purchase an item from a store, the parallel black stripes of varying widths on the item you purchase is called barcode. Barcodes are a method of representing data in a visual, machine-readable format. Barcodes are used to store information about products for easy identification and tracking. Various industries use barcodes for inventory management.

Using Python you can generate barcodes, scan and read the content of a barcode.

How to Generate and Customize Barcodes

The following steps show how to generate barcodes using the python-barcode library.

1. Install the Required Library

Open your terminal or command prompt and run the following pip command to install the required library. Ensure you have pip installed on your machine.

 pip install python-barcode

2. Import the Required Modules

In your script, include the following import statements to import the modules needed for barcode generation.

 import barcode
from the barcode.writer import ImageWriter

Writers handle the generation and saving of barcode images in different formats. The python-barcode library provides different barcode writers. Here you’re going to use the ImageWriter class which renders barcodes as images.

3. Code to Generate Barcode

The python-barcode library offers various barcode formats, such as Code39, Code128, EAN13, and ISBN-10 for generating barcodes.

 def generate_barcode(data, barcode_format, options=None):
    
    barcode_class = barcode.get_barcode_class(barcode_format)
    
    barcode_image = barcode_class(data, writer=ImageWriter())
    
    barcode_image.save("barcode", options=options)

The generate_barcode function generates a barcode based on the given data and format (barcode_format) and saves the barcode image to a file, barcode.png. The file extension depends on the writer class you use.

4. Generate and Customize Barcode

To generate a barcode, call the generate_barcode function and pass the required parameters.

 generate_barcode("MakeUseOf", "code128")

Writers take several options that allow you to customize barcodes. Customization options include modifying the size, font, color of the barcode, and so on. You can refer to the python-barcode documentation to access the complete list of common writer options.

 
generate_barcode("MakeUseOf", "code128", options={"foreground":"red",
                                                  "center_text": False,
                                                  "module_width":0.4,
                                                  "module_height":20})

How to Scan and Decode Barcodes

The following steps show how to scan and decode barcodes using the Python pyzbar library.

1. Install the Required Libraries

To scan and decode barcodes, you need to install the following libraries:

 brew install zbar 
sudo apt-get install libzbar0
pip install pyzbar opencv-python

2. Import the Required Modules

After installing the libraries, add the following import statements to your script to import the necessary modules.

 import cv2
from pyzbar import pyzbar

3. Scan Barcodes From Images

To scan barcodes from image files:

  1. Load the image using OpenCV’s imread function. This returns an instance of numpy.ndarray.
  2. Pass the output array to pyzbar.decode for detection and decoding. You can also pass instances of PIL.Image.
 def scan_barcode_from_image(image_path):
    
    image = cv2.imread(image_path)
    
    barcodes = pyzbar.decode(image)
    
    for barcode in barcodes:
        
        barcode_data = barcode.data.decode("utf-8")
        barcode_type = barcode.type
        print("Barcode Data:", barcode_data)
        print("Barcode Type:", barcode_type)

The function takes an image_path parameter, reads the image, decodes any barcodes present in the image, and prints the decoded data and barcode type for each detected barcode.

 scan_barcode_from_image("barcode.png")
> Barcode Data: MakeUseOf
> Barcode Type: CODE128

4. Scan Barcodes From Webcam Stream

You can also scan and read barcodes in real-time from a webcam stream with the help of the Python OpenCV library.

 def scan_barcode_from_webcam():
    
    video_capture = cv2.VideoCapture(0)

    while True:
        
        _, frame = video_capture.read()

        
        barcodes = pyzbar.decode(frame)

        
        for barcode in barcodes:
            
            barcode_data = barcode.data.decode("utf-8")
            barcode_type = barcode.type
            print("Barcode Data:", barcode_data)
            print("Barcode Type:", barcode_type)

        
        if cv2.waitKey(1) & 0xFF == ord("q"):
            break

    
    video_capture.release()
    cv2.destroyAllWindows()


scan_barcode_from_webcam()

The scan_barcode_from_webcam function continuously captures frames from the webcam, decodes any barcodes present in the frame, extracts information about the barcode, and prints the information. To quit press the letter q on your keyboard.

Generating Barcodes and QR Codes in Python

With Python, generating and reading barcodes becomes accessible and efficient. By following the steps outlined, you can generate a variety of barcodes to suit your needs.

QR codes (Quick Response codes) are two-dimensional barcodes that can be scanned and read by smartphones, tablets, or other devices equipped with a camera and a QR code reader application. Using the Python qrcode library you can generate, scan and read QR Codes efficiently.

MakeUseOf

A Guide to Encryption and Hashing in Laravel

https://ashallendesign.ams3.cdn.digitaloceanspaces.com/public/blog/128/a-guide-to-encryption-and-hashing-in-laravel.png

Every web developer should be aware of hashing and encryption because they are vital security concepts. When used correctly, they can improve web application security and help ensure the privacy of users’ personally identifiable information (PII).

In this article, we’ll explore hashing and encryption, including their differences. We’ll also learn how to use them within Laravel projects to improve application security. We’ll cover how to hash data and compare it to plain-text values. We’ll also explain the different ways that you can encrypt and decrypt data in Laravel.

What Are Hashing and Encryption?

As a web developer, you’ve likely encountered the terms “encryption” and “hashing”. You may have even used them in your own projects, but what do they mean, and how are they different?

These terms are sometimes used interchangeably, but they are different techniques with their own use cases.

Encryption and hashing are both forms of “cryptographic security” and consist of using mathematical algorithms to transform data into a form that is unreadable to humans and (mostly) secure.

The world of cryptography is complex and relies heavily on mathematics. Therefore, for the sake of this article, we’ll keep things simple and focus on the high-level concepts of both techniques and how they can be used in your Laravel applications.

Encryption

Let’s start by taking a look at encryption.

Encryption is the process of encoding information (usually referred to as “plaintext”) into an alternative form (usually referred to as “ciphertext”). Typically, ciphertext is generated using a “key” and an encryption algorithm. It can then only be decrypted and read by someone who has the correct key, depending on the type of encryption used.

Encryption can come in two forms: “symmetric” and “asymmetric”.

Symmetric encryption uses the same key for both encryption and decryption. This means that the same key is used to encrypt and decrypt the data. Examples of this type of encryption include Advanced Encryption Standard (AES) and Data Encryption Standard (DES). Laravel uses AES-256-CBC encryption by default.

Asymmetric encryption is a bit more complicated and uses a pair of keys: a public key and a private key. The public key is used to encrypt the data, and the private key is used to decrypt the data. Thus, the public key can be shared with anyone, but the private key must be kept secret, as anyone with it can successfully decrypt the data. Examples of this type of encryption include Rivest–Shamir–Adleman (RSA) and Diffie-Hellman.

As an example of what an encrypted string might look like, let’s say that we have a string of text that we want to encrypt. We’ll use the following string as an example:

        

1This is a secret message.

If we encrypted this text using AES-256-CBC encryption and used U2x2QdvosFTtk5nL0ejrKqLFP1tUDtSt as the key, we would get the following ciphertext:

        

1eyJpdiI6IjF1cmF0YU5TMkRnR3NUMVRMMm1udFE9PSIsInZhbHVlIjoieGloYW5VVWtXV2hjcVRVY3hGTkZ1bDdoOVBSZEo1VkVWNE1LSlB5S0lhNkF3SloxeWhRejNwbjN5SEgxeUJXayIsIm1hYyI6IjljNzY0MTBmMGJlZmRjNzcwMjFiMmFjYmJhNTNkNWVhODkxMTgzYmYwMjA3N2YzMjM1YmVhZWU4NDRiOTYzZWQiLCJ0YWciOiIifQ==

If we were then to decrypt the above ciphertext using the same key, we would get the original plaintext:

        

1This is a secret message.

Hashing

Hashing is a bit different from encryption. It is a one-way process, meaning that when data is “hashed”, it’s transformed into what is referred to as a “hash value”. However, unlike encryption, you cannot reverse the process and get the original data. Hence, the hash value is not reversible.

Typically, you’d want to use a hash in a situation where you don’t want any data to be retrieved in the event of a leak. For example, you may want to store a user’s password in your database but wouldn’t want to store it in plain text in case the database was ever compromised. If you did store it in plain text, you would potentially be giving malicious hackers a list of passwords in use. In an ideal world, all users would use unique passwords, but we all know that this isn’t the case. Therefore, a leaked database of plain-text passwords would be a goldmine for hackers because they could then try to use the passwords on other websites or applications. To combat this problem, we store passwords in a hashed format.

For example, let’s say that a user’s password is “hello”. If we were to use the bcrypt algorithm to hash this password, it would look something like this:

        

1$2y$10$XY2DMYKvLrMj7yqYwyvK5OSvKTA6HOoPTpe3gVpVP5.Y4kN1nbOLq

An important part of hashing is the use of a “salt”. A salt is a random string of characters used to make the hashed values of the same data different. This means that if two users have the same password, their hashed passwords would be stored differently in the database. This is important because it would stop hackers from being able to deduce which users had the same password. For instance, the following strings are all hashed versions of hello:

        

1$2y$10$NUgYbLrzxn471GzcIN10wedXEcltcbAasHqU7hCeMFv4aCTl/6bVW

        

1$2y$10$AvBxO6HCRwYPNPZmeERIEOzLAJP7ZkcjrekdzaRLwY8YX4m9VJiFy

        

1$2y$10$YQ3lzNx8h0tDgw4K3dzJAOxycZhDhTAnueSugbmoo3NDTuq1OT8KW

You’re probably thinking, “If it’s hashed and can’t be reversed, how do we know if the password is correct?”. We can do this by comparing the hash value of the password that the user has entered with the hash value of the password that is stored in the database by using the password_verify() function PHP provides. If they match, then we know that the password is correct. If they don’t match, then we know that the password is incorrect. Later in this article, we’ll cover how you can do this comparison in Laravel.

Many hashing algorithms are available, such as bcrypt, argon2i, argon2id, md5, sha1, sha256, sha512, and many more. However, when dealing with passwords, you should always use a hashing algorithm designed to be slow, such as bcrypt, because it makes it more difficult for hackers to brute-force the passwords.

Hashing in Laravel

Now that we have a basic idea of what hashing is, let’s take a look at how hashing works within the Laravel framework.

Hashing Passwords

As mentioned previously, you don’t want to store users’ passwords as plain text in a database. Instead, you’ll want to store their passwords in a hashed format. This is where the Hash facade comes into play.

By default, Laravel supports three different hashing algorithms: “Bcrypt”, “Argon2i”, and “Argon2id”. By default, Laravel uses the “Bcrypt” algorithm, but if you want to use a different one, Laravel allows you to change it. We’ll cover this topic in more detail later in the article.

To get started with hashing a value, you can use the Hash facade. For example, let’s say that we want to hash the password “hello”:

        

1use Illuminate\Support\Facades\Hash;

2 

3$hashedValue = Hash::make('hello');

4 

5// $hashedValue = $2y$10$XY2DMYKvLrMj7yqYwyvK5OSvKTA6HOoPTpe3gVpVP5.Y4kN1nbOLq

As you can see, it’s really easy to hash a value.

To give this a bit of context, let’s look at how it might work in a controller in your application. Imagine that you have a PasswordController that allows authenticated users to update their password. The controller may look something like so:

        

1namespace App\Http\Controllers;

2 

3use App\Http\Controllers\Controller;

4use Illuminate\Http\Request;

5use Illuminate\Support\Facades\Hash;

6 

7final class PasswordController extends Controller

8{

9 public function update(Request $request)

10 {

11 // Validate the new password here...

12 

13 $request->user()->fill([

14 'password' => Hash::make($request->newPassword)

15 ])->save();

16 }

17}

Comparing Hashed Values to Plain Text

As previously mentioned, it’s not possible to reverse a hashed value. Therefore, if we want to determine the hashed value, we can only do so by verifying the hashed value against a plain-text value. This is where the Hash::check() method comes into play.

Let’s imagine that we want to determine whether the “hello” plain-text password is the same as the hashed password that we created earlier. We can do this by using the Hash::check() method:

        

1$plainTextPassword = 'hello';

2$hashedPassword = "$2y$10$XY2DMYKvLrMj7yqYwyvK5OSvKTA6HOoPTpe3gVpVP5.Y4kN1nbOLq";

3 

4if (Hash::check($plainTextPassword, $hashedPassword)) {

5 // The passwords match...

6} else {

7 // The passwords do not match...

8}

The Hash::check() method will return true if the plain-text password matches the hashed password. Otherwise, it will return false.

This type of approach is what would typically be used when a user is logging into your application. If we were to ignore any additional security measures (such as rate limiting) for our login form, your login flow might be following the steps below:

  • The user provides an email and password.
  • Attempt to retrieve a user from the database with the given email.
  • If a user is found, then compare the given password with the hashed password stored in the database by using the Hash::check method.
  • If the Hash::check method returns true, then the user has successfully logged in. Otherwise, they’ve entered the incorrect password.

Hash Drivers in Laravel

Laravel provides the functionality for you to choose between different hashing algorithms. By default, Laravel uses the “Bcrypt” algorithm, but you can change it to either the “Argon2i” or the “Argon2id” algorithm, which are also supported. Alternatively, you can implement your own hashing algorithm, but this is strongly discouraged because you may introduce security vulnerabilities into your application. Instead, you should use one of the algorithms provided through PHP so that you can be sure the algorithms are tried and tested.

To change the hashing algorithm being used across your entire application, you can change the driver value in the config/hashing.php config file. The default value is bcrypt, but you can change this to either argon or argon2id, like so:

        

1return [

2 

3 // ...

4 

5 'driver' => 'bcrypt',

6 

7 // ...

8 

9];

Alternatively, if you’d prefer to explicitly define the algorithm that should be used, you can use the driver method on Hash facade to determine which hashing driver is used. For example, if you wanted to use the “Argon2i” algorithm, you could do the following:

        

1$hashedValue = Hash::driver('argon')->make('hello');

Encryption in Laravel

Let’s now take a look at how to use encryption in Laravel.

Encrypting and Decrypting Values

To get started with encrypting values in Laravel, you can make use of the encrypt() and decrypt() helper functions or the Crypt facade, which provides the same functionality. For example, let’s say that we want to encrypt the string “hello”:

        

1$encryptedValue = encrypt('hello');

By running the above, the encryptedValue variable would now be equal to something like this:

        

1eyJpdiI6IitBcjVRanJTN3hTdnV6REdScVZFMFE9PSIsInZhbHVlIjoiZGcycC9pTmNKRjU3RWpmeW1GdFErdz09IiwibWFjIjoiODg2N2U0ZTQ1NDM3YjhhNTFjMjFmNmE4OTA2NDI0NzRhZmI2YTg5NzEwYjdmY2VlMjFhMGZhYzE5MGI2NDA3NCIsInRhZyI6IiJ9

Now that we have the encrypted value, we can decrypt it by using the decrypt() helper function:

        

1$encryptedValue = 'eyJpdiI6IitBcjVRanJTN3hTdnV6REdScVZFMFE9PSIsInZhbHVlIjoiZGcycC9pTmNKRjU3RWpmeW1GdFErdz09IiwibWFjIjoiODg2N2U0ZTQ1NDM3YjhhNTFjMjFmNmE4OTA2NDI0NzRhZmI2YTg5NzEwYjdmY2VlMjFhMGZhYzE5MGI2NDA3NCIsInRhZyI6IiJ9';

2 

3$decryptedValue = decrypt($encryptedValue);

By running the above, the decryptedValue variable would now be equal to "hello".

If any incorrect data were passed to the decrypt() helper function, an Illuminate\Contracts\Encryption\DecryptException exception would be thrown.

As you can see, encrypting and decrypting data in Laravel is relatively easy. It can simplify the process of storing sensitive data in your database and then decrypting it when you need to use it.

Changing the Encryption Key and Algorithm

As mentioned earlier in the article, Laravel uses the “AES-256” symmetric encryption algorithm by default, meaning that a single key is used for encrypting and decrypting data. By default, this key is the APP_KEY value defined in your .env file. This is very important to remember because if you change your application’s APP_KEY, then any encrypted data that you have stored can no longer be decrypted (without making changes to your code to explicitly use the older key).

If you wish to change the encryption key without changing your APP_KEY, you can do so by changing the key value in the config/app.php config file. Likewise, you can also change the cipher value to specify the encryption algorithm used. The default value is AES-256-CBC, but can be changed to AES-128-CBC, AES-128-GCM, or AES-256-GCM.

Automatically Encrypting Model Attributes

If your application is storing sensitive information in the database, such as API keys or PII, you can use make use of Laravel’s encrypted model cast. This model cast automatically encrypts data before storing it in the database and then decrypts it when it’s retrieved.

This is a useful feature because it allows you to continue working with your data as if it weren’t encrypted, but it’s stored in an encrypted format.

For example, to encrypt the my_secret_field on a User model, you could update your model like so:

        

1class User extends Model

2{

3 protected $casts = [

4 'my_secret_field' => 'encrypted',

5 ];

6}

Now that it’s defined as an accessor, you can continue using the field as usual. Thus, if we wanted to update the value stored in the my_secret_field, we could still use the update method like so:

        

1$user->update(['my_secret_field' => 'hello123']);

Notice how we didn’t need to encrypt the data before passing it to the update method.

If you were to now inspect the row in the database, you wouldn’t see "hello123" in the user’s my_secret_field field. Instead, you’d see an encrypted version of it, such as the following:

        

1eyJpdiI6IjM3MUxuV0lKc2RjSGNYT2dXanhKeXc9PSIsInZhbHVlIjoiNmxPZjUray9ZV21Ba1RnRkFNdHRTZz09IiwibWFjIjoiNTNlNmU0YTY5OGFjZWU2OGJiYzY4OWYzYzExYjMzNTI0MDQ2YTJiM2M4YWZkMjkyMGQxNmQ2MmYwNzQyNGFjYSIsInRhZyI6IiJ9

Thanks to the encrypted model cast, we would still be able to use the intended value of the field. For example,

        

1$result = $user->my_secret_field;

2 

3// $result is equal to: "hello123"

As you can imagine, using the encrypted model cast is a great way to quickly add encryption to an application without having to manually encrypt and decrypt the data. Hence, it is a quick way to improve data security.

However, it does have some limitations, of which you should be aware. First, because the encryption and decryption are run when storing and fetching the Model, calls using the DB facade won’t automatically do the same. Therefore, if you intend to make any queries to the database using the DB facade (rather than using something like Model::find() or Model::get()), you’ll need to manually handle the encryption.

Furthermore, it’s worth noting that although encrypting fields in the database improves security, it doesn’t mean the data are completely secure. If a malicious attacker finds the encryption key, they could decrypt the data and access the sensitive information. Therefore, encrypting the fields is beneficial only if the database is compromised. If you’re using a key (such as your application’s APP_KEY) that’s stored in your .env file, then a breach of your application server would also allow the attacker to decrypt the data.

Using a Custom Encryption Key When Manually Encrypting Data

When encrypting and decrypting data, there may be times when you want to use a custom encryption key. You might want to do this to avoid coupling your encrypted data to your application’s APP_KEY value. Alternatively, you may want to give users the ability to define their own encryption keys so that (theoretically) only they can decrypt their own data.

If you’re manually encrypting and decrypting data, to define your own encryption key, you can instantiate the \Illuminate\Encryption\Encrypter class and pass the key to the constructor. For example, let’s imagine that we want to encrypt some data using a different encryption key. Our code may look something like so:

        

1use Illuminate\Encryption\Encrypter;

2 

3// Our custom encryption key:

4$key = 'U2x2QdvosFTtk5nL0ejrKqLFP1tUDtSt';

5 

6$encrypter = new Encrypter(

7 key: $key,

8 cipher: config('app.cipher'),

9);

10 

11$encryptedValue = $encrypter->encrypt('hello');

As you can see, this is easy to do and adds the flexibility to use a custom encryption key. Now, if we were to try and decrypt the $encryptedValue variable, we would need to use the same key that we used to encrypt it and wouldn’t be able to run the decrypt() helper function because it wouldn’t be using the correct key.

Using a Custom Encryption Key When Using Model Casts

If you’re using the encrypted model cast, as we’ve covered earlier in this article, and you want to use a custom key for the encrypted fields, you can define your own encrypter for Laravel to use by using the Model::encryptUsing method.

We’d typically want to do this within a service provider (such as the App\Providers\AppServiceProvider) so that the custom encrypter is defined and ready to use when the application starts.

Let’s take a look at an example of how we could use the Model::encryptUsing method within our AppServiceProvider:

        

1declare(strict_types=1);

2 

3namespace App\Providers;

4 

5use Illuminate\Database\Eloquent\Model;

6use Illuminate\Encryption\Encrypter;

7 

8final class AppServiceProvider extends ServiceProvider

9{

10 public function boot(): void

11 {

12 $this->defineCustomModelEncrypter();

13 }

14 

15 private function defineCustomModelEncrypter(): void

16 {

17 // Our custom encryption key:

18 $key = 'U2x2QdvosFTtk5nL0ejrKqLFP1tUDtSt';

19 

20 $encrypter = new Encrypter(

21 key: $key,

22 cipher: config('app.cipher'),

23 );

24 

25 Model::encryptUsing($encrypter);

26 }

27 

28 // ...

29}

As you can see in the example above, defining the custom encrypter is very similar to how we defined it manually earlier in this article. The only difference is that we’re passing the Encrypter object to the Model::encryptUsing method so that Laravel can use it behind-the-scenes for us.

Encrypting Your .env File

As of Laravel 9.32.0, your application’s .env file can also be encrypted. This is useful if you want to store sensitive information in your .env file to your source control (such as git) but don’t want to store it in plain text. This is also beneficial because it allows a local versions of your .env config variables to be shared with other developers on your team for local development purposes.

To encrypt your environment file, you’ll need to run the following command in your project root:

This will generate an output similar to this:

        

1INFO Environment successfully encrypted.

2

3Key ........................ base64:amNvB/EvaX1xU+5R9Z37MeKR8gyeIRxh1Ku0pqNlK1Y

4Cipher ............................................................ AES-256-CBC

5Encrypted file ................................................. .env.encrypted

As we can see from the output, the command has used the key base64:amNvB/EvaX1xU+5R9Z37MeKR8gyeIRxh1Ku0pqNlK1Y to encrypt the .env file using the AES-256-CBC cipher and then stored the encrypted value in the .env.encrypted file.

This means that we can now store that file in our source control, and other developers on our team can use the same key to decrypt the file and use the values within it.

To decrypt the file, you need to run the php artisan env:decrypt command and pass the key used to encrypt the file. For example,

        

1php artisan env:decrypt base64:amNvB/EvaX1xU+5R9Z37MeKR8gyeIRxh1Ku0pqNlK1Y

This will then decrypt the .env.encrypted file and store the decrypted values in the .env file.

Conclusion

Hopefully, reading this article has given you a basic understanding hashing and encryption, as well as the differences between them. It should also have given you the confidence to use both concepts within Laravel projects to improve the security of your applications.

Laravel News Links

12 Laravel security best practices for 2023

https://res.cloudinary.com/benjamin-crozat/image/upload/dpr_auto,f_auto,q_auto,w_auto/v1690645860/dhjbi100_uiarfm.png

Don’t track your .env file

Your .env file contains sensitive information.

Please, don’t track it!

Make sure it’s included in your .gitignore.

Most of the time, data leaks are inside jobs.

A password manager is a better solution for sharing credentials.

If you want your team members to have access to a curated set of sensitive information, use a password manager with a proven track record of rock-solid security.

Keep Laravel up to date

Keeping Laravel up to date allows you to stay in touch with the latest security updates.

Make sure you are running a version of Laravel that is still being patched, and you should be OK.

If you never upgraded a Laravel project, I wrote a guide that will teach you how to do it.

Keep your first and third-party packages up to date

Access to dozens of packages from the official Laravel ecosystem and thousands of community packages is what makes our job easier.

But the more packages you use, the more points of failure you can be subject to.

Regularly running composer update goes a long way toward a more secure codebase.

composer update in action.

Disable debug messages in production

Make sure these two environment variables are correctly set in production.

APP_ENV=production

APP_DEBUG=false

You don’t want to leak information about your app’s architecture or configuration. Usually, debug messages contain a lot of this kind of details.

Don’t send sensitive information to error monitoring tools

Talking about sensitive information in debug messages, we haven’t eradicated them yet.

If you are using an error monitoring tool, you have to them there as well.

PHP 8.2 introduced a new attribute, \SensitiveParameter, that can hide anything from the stack trace (which is sent to error monitoring tools).

function something(

#[\SensitiveParameter]

$top_secret_parameter,

$who_cares_parameter,

) {

throw new \Exception('Whoops, something bad happended!');

}

Restrict parts of your app with policies

Policies in Laravel are like nightclub bouncers preventing people from accessing restricted areas.

Let’s look at a real-world example of using a Policy:

// app/Policies/PostPolicy.php

public function update(User $user, Post $post)

{

return $user->id === $post->user_id;

}

 

// app/Http/Controllers/PostController.php

public function update(Request $request, Post $post)

{

$this->authorize('update', $post);

 

// ...

}

As you can see, they make it really easy to check for permission before allowing a user to do anything.

Make sure to check the documentation, because there’s a lot to learn about Policies.

Protect your forms from cross-site request forgery (CSRF)

You might want to use the @csrf Blade directive in your forms.

<form method="POST" action="">

@csrf

 

<p>

<label for="name">First Name</label>

<input type="text" id="name" name="name" value="" required />

</p>

 

</form>

This directive generates a hidden input field containing a CSRF token automatically included when submitting the form.

This token confirms that the form is being submitted from your application and not by a third party.

The verification is handled by the VerifyCsrfToken middleware that Laravel uses by default for all your web routes.

Learn more about CSRF protection in Laravel’s documentation.

Validate the user’s input

Validation in Laravel is crucial in ensuring your application’s security.

Validation rules are numerous and will help you sanitize the data your users send with ease. Because you know the drill, right? Never trust your users’ input.

use Illuminate\Http\Request;

 

class PostController extends Controller

{

function store(Request $request)

{

$validated = $request->validate([

'user_id' => 'required|exists:users,id',

'title' => 'required|string|min:3|max:255',

'content' => 'required|string|min:3',

'published' => 'sometimes|boolean'

]);

 

Post::create($validated);

 

//

}

}

Learn more about validation in Laravel on the official documentation.

Be careful with uploaded files

As we saw, the user’s input must never be trusted. That also goes for the files they upload. Here are a few recommendations:

  1. Check the file’s MIME type (Laravel has the right validation rules for that).

$request->validate([

'file' => 'required|mimes:gif,jpeg,png,webp',

]);

  1. When possible, don’t make uploaded files publicly accessible (using the local file driver, for instance).

  2. Upload files on another server. If a hacker bypasses your securities, they won’t be able to run unauthorized code and access sensitive information.

  3. Delegate file uploads to a third-party service reputed for its security (meaning they never leaked data).

Encrypt the payload of your jobs

Whenever you dispatch a job, its payload is saved into the database, Redis or whatever else you told Laravel to use using the QUEUE_DRIVER environment variable.

The payload may contain sensitive information that any of your employee may look at and potentially misuse. As I said in the beginning of this article, leaks are often initiated by employees.

Fortunately, Laravel provides a the Illuminate\Contracts\Queue\ShouldBeEncrypted Contract, which will automatically encrypt the payloads. To make sure nobody can unencrypt them, make sure the Laravel’s APP_KEY defined in your .env is unaccessible to anyone besides a few trusted people.

use Illuminate\Contracts\Queue\ShouldQueue;

use Illuminate\Contracts\Queue\ShouldBeEncrypted;

 

class SomeJob implements ShouldQueue, ShouldBeEncrypted

{

//

}

Write tests for security risks

Testing is unfortunately a vast and lesser-known topic among developers.

Automatically testing multiple scenarii for potential security breaches is a great way to make sure they stay closed.

Laracasts provides free testing courses to help you get started. One with PHPUnit, the industry standard, and one with Pest, the best testing framework on this planet that modernizes and simplifies testing in PHP.

Keep your project tested

Do regular security audits

This practice is one of the most efficient and should be mandatory for anyone that is really serious about security. External feedback can be eyes opening.

As you can imagine, doing security audits isn’t free. It might only be worth it for enterprise since it would cost even more to pay for the potential fines! You cannot put a price on maintaining a good reputation and the trust of your users.

Laravel News Links

MySQL Capacity Planning

https://www.percona.com/blog/wp-content/uploads/2023/08/MySQL-capacity-planning-150×150.jpgMySQL capacity planning

As businesses grow and develop, the requirements that they have for their data platform grow along with it. As such, one of the more common questions I get from my clients is whether or not their system will be able to endure an anticipated load increase. Or worse yet, sometimes I get questions about regaining normal operations after a traffic increase caused performance destabilization.

As the subject of this blog post suggests, this all comes down to proper capacity planning. Unfortunately, this topic is more of an art than a science, given that there is really no foolproof algorithm or approach that can tell you exactly where you might hit a bottleneck with server performance. But we can discuss common bottlenecks, how to assess them, and have a better understanding as to why proactive monitoring is so important when it comes to responding to traffic growth.

Hardware considerations

The first thing we have to consider here is the resources that the underlying host provides to the database. Let’s take a look at each common resource. In each case, I’ll explain why a 2x increase in traffic doesn’t necessarily mean you’ll have a 2x increase in resource consumption.

Memory

Memory is one of the easier resources to predict and forecast and one of the few places where an algorithm might help you, but for this, we need to know a bit about how MySQL uses memory.

MySQL has two main memory consumers. Global caches like the InnoDB buffer pool and MyISAM key cache and session-level caches like the sort buffer, join buffer, random read buffer, etc.

Global memory caches are static in size as they are defined solely by the configuration of the database itself. What this means is that if you have a buffer pool set to 64Gb, having an increase in traffic isn’t going to make this any bigger or smaller. What changes is how session-level caches are allocated, which may result in larger memory consumption.

A tool that was popular at one time for calculating memory consumption was mysqlcalculator.com. Using this tool, you could enter in your values for your global and session variables and the number of max connections, and it would return the amount of memory that MySQL would consume. In practice, this calculation doesn’t really work, and that’s due to the fact that caches like the sort buffer and join buffer aren’t allocated when a new connection is made; they are only allocated when a query is run and only if MySQL determines that one or more of the session caches will be needed for that query. So idle connections don’t use much memory at all, and active connections may not use much more if they don’t require any of the session-level caches to complete their query.

The way I get around this is to estimate the amount of memory consumed on average by sessions as such…

({Total memory consumed by MySQL} – {sum of all global caches}) / {average number of active sessions}

Keep in mind that even this isn’t going to be super accurate, but at least it gives you an idea of what common session-level memory usage looks like. If you can figure out what the average memory consumption is per active session, then you can forecast what 2x the number of active sessions will consume.

This sounds simple enough, but in reality, there could be more to consider. Does your traffic increase come with updated code changes that change the queries? Do these queries use more caches? Will your increase in traffic mean more data, and if so, will you need to grow your global cache to ensure more data fits into it?

With the points above under consideration, we know that we can generally predict what MySQL will do with memory under a traffic increase, but there may be changes that could be unforeseen that could change the amount of memory that sessions use.

The solution is proactive monitoring using time-lapse metrics monitoring like what you would get with Percona Monitoring and Management (PMM). Keep an eye on your active session graph and your memory consumption graph and see how they relate to one another. Checking this frequently can help you get a better understanding of how session memory allocation changes over time and will give you a better understanding of what you might need as traffic increases.

CPU

When it comes to CPU, there’s obviously a large number of factors that contribute to usage. The most common is the queries that you run against MySQL itself. However, having a 2x increase in traffic may not lead to a 2x increase in CPU as, like memory, it really depends on the queries that are run against the database. In fact, the most common cause of massive CPU increase that I’ve seen isn’t traffic increase; it’s code changes that introduced inefficient revisions to existing queries or new queries. As such, a 0% increase in traffic can result in full CPU saturation.

This is where proactive monitoring comes into play again. Keep an eye on CPU graphs as traffic increases. In addition, you can collect full query profiles on a regular basis and run them through tools like pt-query-digest or look at the Query Analyzer (QAN) in PMM to keep track of query performance, noting where queries may be less performant than they once were, or when new queries have unexpected high load.

Disk space

A 2x increase in traffic doesn’t mean a 2x increase in disk space consumption. It may increase the rate at which disk space is accumulated, but that also depends on how much of the traffic increase is write-focused. If you have a 4x increase in reads and a 1.05X increase in writes, then you don’t need to be overly concerned about disk space consumption rates.

Once again, we look at proactive monitoring to help us. Using time-lapse metrics monitoring, we can monitor overall disk consumption and the rate at which consumption occurs and then predict how much time we have left before we run out of space.

Disk IOPS

The amount of disk IOPS your system uses will be somewhat related to how much of your data can fit into memory. Keep in mind that the disk will still need to be used for background operations as well, including writing to the InnoDB redo log, persisting/checkpointing data changes to table spaces from the redo log, etc. But, for example, if you have a large traffic increase that’s read-dependent and all of the data being read in the buffer pool, you may not see much of an IOPS increase at all.

Guess what we should do in this case? If you said “proactive monitoring,” you get a gold star. Keep an eye out for metrics related to IOPS and disk utilization as traffic increases.

Before we move on to the next section, consider the differences in abnormal between disk space and disk IOPS. When you saturate disk IOPS, your system is going to run slow. If you fill up your disk, your database will start throwing errors and may stop working completely. It’s important to understand the difference so you know how to act based on the situation at hand.

Database engine considerations

While resource utilization/saturation are very common bottlenecks for database performance, there are limitations within the engine itself. Row-locking contention is a good example, and you can keep an eye on row-lock wait time metrics in tools like PMM. But, much like any other software that allows for concurrent session usage, there are mutexes/semaphores in the code that are used to limit the number of sessions that can access shared resources. Information about this can be found in the semaphores section in the output of the “SHOW ENGINE INNODB STATUS” command.

Unfortunately, this is the single hardest bottleneck to predict and is based solely on the use case. I’ve seen systems running 25,000+ queries per second with no issue, and I’ve also seen systems running ~5,000 queries per second that ran into issues with mutex contention.

Keeping an eye on metrics for OS context switching will help with this a little bit, but unfortunately this is a situation where you normally don’t know where the wall is until you run right into it. Adjusting variables like innodb_thread_concurrency can help with this in a pinch, but when you get to this point, you really need to look at query efficiency and horizontal scaling strategies.

Another thing to consider is configurable hard limits like max_connections, where you can limit the upper bound of the number of connections that can connect to MySQL at any given time. Keep in mind that increasing this value can impact memory consumption as more connections will use more memory, so use caution when adjusting upward.

Conclusion

Capacity planning is not something you do once a year or more as part of a general exercise. It’s not something you do when management calls you to let you know a big sale is coming up that will increase the load on the hosts. It’s part of a regular day-to-day activity for anyone that’s operating in a database administrator role.

Proactive monitoring plays a big part in capacity planning. I’m not talking about alert-based monitoring that hits your pager when it’s already too late, but evaluating metrics usage on a regular basis to see what the data platform is doing, how it’s handling its current traffic, etc. In most cases, you don’t see massive increases in traffic all at once; typically, it’s gradual enough that you can monitor as it increases and adjust your system or processes to avoid saturation.

Tools like PMM and the Percona Toolkit play a big role in proactive monitoring and are open source for free usage. So if you don’t have tools like this in place, this comes in at a price point that makes tool integration easier for your consideration.

Also, if you still feel concerned about your current capacity planning, you can always reach out to Percona Managed Services for a performance review or query review that will give you a detailed analysis of the current state of your database along with recommendations to keep it as performant as possible.

Percona Monitoring and Management is a best-of-breed open source database monitoring solution. It helps you reduce complexity, optimize performance, and improve the security of your business-critical database environments, no matter where they are located or deployed.

 

Download Percona Monitoring and Management Today

Percona Database Performance Blog

TailwindCraft: Free and Open-Source Prebuilt UI Components

https://tailwindcraft.com/storage/photos/1/cover.pngMeticulously designed open-source UI components powered by Tailwind CSS. Streamlines web development with versatile prebuilt elements, seamless Tailwind CSS integration, and a vibrant open-source community.Laravel News Links

5 Ways to Retrieve the Last Inserted ID in Laravel

https://laracoding.com/wp-content/uploads/2023/07/5-ways-to-retrieve-the-last-inserted-id-in-laravel_1093.png

In Laravel, after inserting data into a database table, you might need to retrieve the last inserted ID after creating the record. This ID is essential for various tasks, like redirecting users to the newly created resource or performing further operations.

This blog post will guide you through several methods to get the last inserted ID in Laravel by using Eloquent models, the DB facade, or direct access to the PDO instance. Let’s explore the various approaches

Method 1: Retrieving Insert ID Using Model::create

One common way to insert data into the database and get the last inserted ID is by using the create method on an Eloquent model. This method not only inserts the data but also automatically fetches the last inserted ID. You can easily access the ID as a property as: $user->id. Consider the following example:

$user = User::create([
    'name' => 'Steve Rogers',
    'email' => 'captain.america@marvel.com',
    'password' => bcrypt('password123'),
]);

$lastInsertedId = $user->id;

Note that when passing an array of properties to the create function, make sure they are defined as fillable, otherwise the values won’t be written into the database. In our example this means we need to ensure the class User properly defines: protected $fillable = ['name', 'email', 'password'];

Method 2: Retrieving Insert ID Using new Model() and save()

Another approach to insert data and retrieve the last inserted ID is by creating a new instance of the model, setting the attributes, and then calling the save method. You can easily access the ID as a property with $user->id. Let’s consider the following example:

$user = new User();
$user->name = 'Natasha Romanoff';
$user->email = 'black.widow@marvel.com';
$user->password = bcrypt('password123');
$user->save();

$lastInsertedId = $user->id;

Method 3: Retrieving Insert ID Using DB Facade insertGetId()

When you need to insert data without using Eloquent models, you can utilize the insertGetId method provided by the DB facade.

$lastInsertedId = DB::table('users')->insertGetId([
    'name' => 'Bruce Banner',
    'email' => 'hulk@marvel.com',
    'password' => bcrypt('password123'),
    'created_at' =>  \Carbon\Carbon::now(),
    'updated_at' => \Carbon\Carbon::now(),
]);

Note that inserting records using this method will not fill your timestamp values automatically, even if they are defined in your Migration. For this reason we’ve included code to generate them manually using Carbon in the code above.

Method 4: Retrieving Insert ID Using Model::insertGetId

If you are inserting data directly through a model and want to retrieve the last inserted ID, you can use the insertGetId method on the model itself.

$lastInsertedId = User::insertGetId([
    'name' => 'Clint Barton',
    'email' => 'hawkeye@marvel.com',
    'password' => 'password123',
    'created_at' =>  \Carbon\Carbon::now(),
    'updated_at' => \Carbon\Carbon::now(),
]);

Note that inserting records using this method will not fill your timestamp values automatically, even if they are defined in your Migration. For this reason we’ve included code to generate them manually using Carbon in the code above.

Method 5: Direct Access to PDO lastInsertId

You can also directly access the PDO instance to retrieve the last inserted ID.

DB::table('users')->insert([
    'name' => 'Peter Parker',
    'email' => 'spiderman@marvel.com',
    'password' => bcrypt('password123'),
    'created_at' =>  \Carbon\Carbon::now(),
    'updated_at' => \Carbon\Carbon::now(),
]);

$lastInsertedId = DB::getPdo()->lastInsertId();

Note that inserting records using this method will not fill your timestamp values automatically, even if they are defined in your Migration. For this reason, we’ve included code to generate them manually using Carbon in the code above.

Conclusion

By following the methods outlined in this blog post, you can easily obtain the last inserted ID using Eloquent models, the DB facade, or direct access to the PDO instance. Choose the method that suits your needs. However, it’s worth noting that method 1 and method 2 are the most commonly used in Laravel applications. Happy coding!

References

Laravel News Links

A Dive Into toRawSql()


reading up on laravel tricks

Fly.io can build and run your Laravel apps globally, including your scheduled tasks. Deploy your Laravel application on Fly.io, you’ll be up and running in minutes!

In the recent past, we were able to dump out the SQL our query builder was generating like so:

$filter = 'wew, dogs';

// Using the `toSql()` helper
DB::table('foo')
  ->select(['id', 'col1', 'col2'])
  ->join('bar', 'foo.bar_id', 'bar.id')
  ->where('foo.some_colum', $filter)
  ->toSql();

// SELECT id, col1, cole2
//     FROM foo
//     INNER JOIN nar on foo.bar_id = bar.id
//     WHERE foo.some_column = ?

This was useful for debugging complicated queries, but note how we didn’t get the value in our WHERE statement!
All we got was a pesky ? – a placeholder for whatever value we’re passing into the query.
The actual value is hidden from us.

“That’s not what I need”, you may have asked yourself. Assuming we aren’t debugging SQL syntax, our query bindings are what we likely care about the most.

Getting the Full Query

New to Laravel 10.15 is the ability to get the full sql query! That’s much more useful for debugging.

$filter = 'wew, dogs';

// Using the `toRawSql()` helper
DB::table('foo')
  ->select(['id', 'col1', 'col2'])
  ->join('bar', 'foo.bar_id', 'bar.id')
  ->where('foo.some_colum', $filter)
  ->toRawSql();

// SELECT "id", "col1", "col2"
//     FROM "foo" 
//     INNER JOIN "bar" ON "foo"."bar_id" = "bar"."id"
//     WHERE "foo"."some_colum" = 'wew, dogs'"

Much better!

The trick to this is that PDO (the core library used to connect to databases) doesn’t just give this to us – we can only get the SQL with the binding placeholders (hence the old toSql() limitation).

So, we need to build the query with our values within the query ourselves! How’s that done?

How It’s Done

This is tricky business, as we’re dealing with user input – any crazy thing a developer (or their users)
might throw into a sql query needs to be properly escaped.

The new toRawSql() helper stuffs the important logic into a method named substituteBindingsIntoRawSql(). Here’s the PR, for reference.

If we dig into that method code a bit, we’ll
see what’s going on!

The first thing the function does is escape all of the values. This lets Laravel print out the query as a string without worrying about mis-aligned quotes or similar issues.

$bindings = array_map(fn ($value) => $this->escape($value), $bindings);

The call to $this->escape() goes down to a database connection object, and deeper into the underlying PDO object. PDO does the work of
actually escaping the query values in a safe way.

You can get a “Connection Refused” error using the toRawSql() method if your database connection isn’t configured or isn’t working.
That’s because the underlying code uses the “connection” object (PDO under the hood) to escape characters within the output.

Following the escaping-of-values, the method goes through the query character by character!

for ($i = 0; $i < strlen($sql); $i++) {
    $char = $sql[$i];
    $nextChar = $sql[$i + 1] ?? null;

    // and so on
}

The major supported databases use different conventions for escape characters. The code here attempts to find escaped characters and ignore them, lest it tries to
substitute something that looks like a query binding character but isn’t. This is the most fraught bit of code in this new feature.

$query = '';


for ($i = 0; $i < strlen($sql); $i++) {
    $char = $sql[$i];
    $nextChar = $sql[$i + 1] ?? null;

    // Single quotes can be escaped as '' according to the SQL standard while
    // MySQL uses \'. Postgres has operators like ?| that must get encoded
    // in PHP like ??|. We should skip over the escaped characters here.
    if (in_array($char.$nextChar, ["\'", "''", '??'])) {
        // We are building the query string back up - We ignore escaped characters
        // and append them to our rebuilt query string. Since we append
        // two characters, we `$i += 1` so the loop skips $nextChar in our `for` loop
        $query .= $char.$nextChar;
        $i += 1;
    } ...
}

The for loop is rebuilding the query string, but with values substituted in for their ? placeholders. The first check here
is looking for certain escape characters. It needs to know the current character AND the next one to know if it’s an escaped character
and therefore should not do any substitutions.

The next part of our conditional is this:

} elseif ($char === "'") { // Starting / leaving string literal...
    $query .= $char;
    $isStringLiteral = ! $isStringLiteral;
}

If we’re opening an unescaped quote, it means we’re at the start (or end) of a string literal. We set a flag for this case, which
is important in our next check.

elseif ($char === '?' && ! $isStringLiteral) { // Substitutable binding...
    $query .= array_shift($bindings) ?? '?';
}

Here’s the magic. If we’re NOT inside of a string literal, AND we’re not finding an escaped character, AND we find a ? character,
then we can assume it’s a query binding to be substituted with the actual value. We take our array of values and shift it – we remove the
first item in that array and append its value to our query string. (The array of values are in the same order of query binding characters – ? – in the query).

Finally, if we just have a regular character that doesn’t have special meaning, we just append it to our query string:

else { // Normal character...
    $query .= $char;
}

Fly.io ❤️ Laravel

Fly your servers close to your users—and marvel at the speed of close proximity. Deploy globally on Fly in minutes!


Deploy your Laravel app!  

Here’s the whole method, as it stands as I write this:

public function substituteBindingsIntoRawSql($sql, $bindings)
{
    $bindings = array_map(fn ($value) => $this->escape($value), $bindings);

    $query = '';

    $isStringLiteral = false;

    for ($i = 0; $i < strlen($sql); $i++) {
        $char = $sql[$i];
        $nextChar = $sql[$i + 1] ?? null;

        // Single quotes can be escaped as '' according to the SQL standard while
        // MySQL uses \'. Postgres has operators like ?| that must get encoded
        // in PHP like ??|. We should skip over the escaped characters here.
        if (in_array($char.$nextChar, ["\'", "''", '??'])) {
            $query .= $char.$nextChar;
            $i += 1;
        } elseif ($char === "'") { // Starting / leaving string literal...
            $query .= $char;
            $isStringLiteral = ! $isStringLiteral;
        } elseif ($char === '?' && ! $isStringLiteral) { // Substitutable binding...
            $query .= array_shift($bindings) ?? '?';
        } else { // Normal character...
            $query .= $char;
        }
    }

    return $query;
}

That’s basically all there is to the story. We want the entire query to be available with our values!
Here are some (light) caveats we have to get this feature:

  1. We need to make a database connection (thanks to using the PDO library for safe escaping)
  2. The above code MAY contain the occasional bug depending on what values are used
  3. Very long (e.g. binary) values in a query would likely return a complete mess of a query string

Those trade-offs seem fine to me!

Laravel News Links