| # | complex | simple |
|---|---|---|
| 1 | Mobile app | Progressive Web App (PWA) |
| 2 | MySQL/PostgreSQL | SQLite |
| 3 | Queue worker | defer() |
| 4 | Broadcast with events | Echo whisper |
| 5 | Mailgun | PHPMailer + gmail |
| 6 | ElasticSearch | SQL queries |
Sunday, September 22, 2024
What tech to use when you are just starting
Friday, September 20, 2024
The role of the CTO
The role of the Chief Technology Officer (CTO):
- Understand business goals, assess feasibility, and set technical priorities accordingly. Create a technical roadmap.
- In the early stages of the company, be the lead engineer. As the company grows, hire technical personnel, provide guidance to the team.
- Review team outputs (code & documentation), ensure effort is aligned with priorities and outputs are clean and of high quality.
- Review tests and verify that important test cases are included.
- In case of personnel loss, temporarily fill the gap until a replacement is hired.
- Oversee IT, CI/CD infrastructure and security.
- Stay updated with technological trends.
- Represent the company's technological vision to partners, investors, or at industry events.
Thursday, September 19, 2024
Financial projection for startups
Friday, August 30, 2024
Why use SQLite?
SQLite is optimized for situations where the workload is predominantly reads with occasional writes, making it ideal for small to medium-sized applications. SQLite is very efficient and can operate comfortably within a few megabytes of RAM.
For highly concurrent environments where many users need to write to the database simultaneously, SQLite might not be the best choice, and a more robust client-server database like MySQL or PostgreSQL would be more appropriate. Recommended RAM for light use for MySQL is 1GB and for PostgreSQL 2GB, which is about 1000 times more than SQLite.
SQLite is an embedded database that runs in the same process as the application using it. This means it doesn’t require a separate server process to manage database connections, unlike MySQL, which operates as a server and listens for incoming connections on a specific port (e.g. 3306). SQLite databases are stored as files on the local filesystem. When an application wants to interact with an SQLite database, it directly accesses the database file without needing to communicate over a network. So with SQLite, if you run multiple apps, there won't be problems like port conflicts or the need for containers.
SQLite doesn't require authentication mechanisms like usernames and passwords. The database file is accessed directly without any user credentials.
If you want to use a cheap (5$/month) VPS with 512GB RAM, SQLite is your only option. It is not just a toy, it is used in popular apps like nomadlist, see Pieter Levels video. You can optimize SQLite even further.
Music: Mike Oldfield - SentinelFriday, August 23, 2024
Web server vs database server scaling
Every user interaction with a website, such as loading a page, clicking a button, or submitting a form, generates a request to the web server. These interactions are frequent and often involve static assets (like images, CSS, and JavaScript files), rendering HTML, and processing logic. As the number of users increases, the web server has to handle a rapidly growing number of these requests.
While many web requests may involve querying the database, they don't always result in database interactions. For example, pages with static content, content cached in memory, or content generated by the web server without requiring a database query won’t proportionally increase the database load. Additionally, many web requests might use the same data that can be cached on the web server, reducing the number of database queries.
Only dynamic content that requires data retrieval or storage will result in database queries. Since not all web interactions require new data from the database, the database load doesn't increase as rapidly as the web server load.
Initially, it's easier and more effective to scale web servers horizontally to handle increased traffic. The database server can handle a significant amount of load on its own due to its ability to manage data consistency, and because you can optimize performance through caching and other techniques. Once the traffic and data operations reach a certain threshold, you will need to consider scaling your database server as well.
Thursday, August 22, 2024
Adventures with PHP & Laravel
- php artisan serve starts a built-in development server for your Laravel application. It's a convenient way to test and develop your application locally without the need for a full-fledged web server.
- php artisan queue:listen initiates a worker process to listen for and process jobs in your Laravel application's queue. It's crucial for background tasks, email sending, and other time-consuming operations.
- php artisan reverb:start initiates the Laravel Reverb WebSocket server, enabling real-time communication between your Laravel application and clients.
- npm run build optimizes the code for performance, minifies it, and bundles it into a single file or multiple files, making it suitable for deployment to a web server.
$users = User::where('id', '!=', Auth::id())->get();
return view('proceed', compact('users'));
<select id="userSelect" class="form-control">
<option value="" disabled selected>Select user...</option>
@foreach($users as $user)
<option value="{{ $user->id }}">{{ $user->name }}</option>
@endforeach
</select>
Naming conventions are very important. For example, if you want to use parameters in routes/channels.php, you have to use the following syntax:
Broadcast::channel('channelBetweenUsers.{user1_id}.{user2_id}', function ($user, $user1_id, $user2_id) {
return (int) $user->id === (int) $user1_id || (int) $user->id === (int) $user2_id;
});
- Client1 sends a message to Client2 via POST request → web server → router → controller
- Controller adds message to queue via SendMessage::dispatch($message)
- Queue driver stores the job.
- A queue worker gets the job from the stored job queue (via polling for database or polling/push notification for Redis).
- Worker processes job via SendMessage::handle(), obtaining the WebSocket channel via GotMessage::dispatch() → constructor → broadcastOn()
- The worker broadcasts the message to all connected clients via WebSocket channel. The worker maintains a persistent WebSocket connection to the Reverb server, allowing it to broadcast messages efficiently, avoid unnecessary routing through the controller.
- Client 2 receives the message via WebSocket.
PHP constructor property promotion:
class MyClass {
public function __construct(public int $val) {
// No need to manually assign $this->val = $val;
}
}
$obj = new MyClass(5);
echo $obj->val; // Output: 5
Queue workers vs defer(): defer() does not guarantee that messages will be processed in order or without conflicts, especially if multiple users are sending messages simultaneously. Queue workers offer better reliability for handling failed jobs, retries, and monitoring. While defer() might seem like a quick solution, utilizing queue workers is the recommended approach for handling message sending and receiving in a Laravel Reverb chat application to ensure optimal performance and responsiveness.Sunday, August 4, 2024
Process models in web apps and mobile/desktop apps
Web applications use a different process model than mobile/desktop applications. Mobile/desktop apps have a process running until the user exits the app, they are typically stateful, meaning the application maintains its state as long as it is running. In contrast, web apps are generally stateless, meaning each request is independent, and the server does not maintain the state between requests. The server processes a request, sends a response, and then terminates the process handling that request. Multiple users accessing a web app will create multiple independent processes on the server. Web apps are designed to handle many short-lived processes. A web process is typically terminated after 300s (set in web server config) if it has not already sent a response.
REST (Representational State Transfer) is designed to be stateless. This means that each HTTP request from a client to a server must contain all the information needed to understand and process the request. The web server handles each request separately, delegating it to PHP, which terminates the process after the response is sent back to the client. This approach ensures scalability and efficiency, as the server can handle many requests from different users simultaneously. Scalability is achieved through load balancing and distributing requests across multiple servers or processes.
- Request-Response Cycle: Each time a user interacts with your web app (e.g., by clicking on a product), the browser sends an HTTP request to the server.
- Single Process Execution: When the server receives a request, it starts a new process or thread to handle that specific request. This process runs your PHP code, generates the necessary response (usually HTML), and sends it back to the browser.
- End of Process: Once the response is sent back to the client, the PHP process or thread is terminated by the web server. The server does not maintain any long-running processes for individual users between requests.
- No Inter-process Communication: Each request is handled independently, and PHP does not keep any information about previous requests in memory. Any necessary state information (like user sessions) is typically managed using session variables, cookies, or other storage mechanisms like databases or caching systems.
- Session Management: To maintain state (like user login status, shopping cart contents, etc.), PHP uses session management. When a user clicks on a different product, the session data is used to retain the necessary information across multiple requests. When a session is started in PHP (using session_start()), PHP generates a unique session ID for that session. This session ID is then stored in a cookie on the client's browser. The actual session data (such as user information, preferences, etc.) is stored on the server, typically in files or a database. The session data is associated with the session ID. When the client makes subsequent requests to the server, the session ID stored in the cookie is sent along with the request. PHP uses this session ID to retrieve the corresponding session data from the server.
PHP-FPM (PHP FastCGI Process Manager) uses FastCGI. Unlike CGI, which creates a new process for each request, FastCGI keeps processes alive and reuses them for multiple requests. This avoids the overhead of process creation and teardown. PHP-FPM runs a master process responsible for managing the worker processes. These processes are persistent and handle multiple requests over their lifetime. Each incoming request is assigned to an available worker process. After completing a request, the worker process becomes idle and waits for the next request. Note that worker processes do not preserve state, i.e. no data is shared between requests handled by the same or different workers.
Note about cPanel NPROC (nb. of processes): The NPROC limit in cPanel refers to the maximum number of processes that a single user can have running simultaneously. This includes all types of processes, not just web server processes (e.g., background scripts, cron jobs, etc.). While this can indirectly impact the number of concurrent connections your web server can handle, it doesn't directly translate to the number of concurrent connections. Your server might use a threaded model (like Apache's worker MPM or Nginx), where each process can handle multiple connections through threads. The NPROC limit then becomes less directly tied to the number of concurrent connections.
Music: Legendary Pakistani Singer goes Metal [Sanson Ki Mala Pe]
