Skip to content

Commit

Permalink
Enhance the example
Browse files Browse the repository at this point in the history
- Add `cluster-creator` container for cluster creation automation.
- Introduce `redis-insight` container.
- Update and improve README.md.
- Enable `strict` mode in `tsconfig.json`.
- Set up a Development Container.
- Incorporate additional timezones for balanced cluster node utilization.
- Relocate links from header to sidebar.
- Make route an optional catch-all.
  • Loading branch information
better-salmon committed Jul 24, 2024
1 parent 21d2e90 commit 1ef74bd
Show file tree
Hide file tree
Showing 9 changed files with 277 additions and 110 deletions.
28 changes: 28 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/docker-in-docker
{
"name": "Docker in Docker",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/base:bullseye",

"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {
"version": "latest",
"enableNonRootDocker": "true",
"moby": "true"
},
"ghcr.io/devcontainers/features/node:1": {}
}

// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],

// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "docker --version",

// Configure tool-specific properties.
// "customizations": {},

// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}
12 changes: 12 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for more information:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
# https://containers.dev/guide/dependabot

version: 2
updates:
- package-ecosystem: "devcontainers"
directory: "/"
schedule:
interval: weekly
77 changes: 53 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,52 +1,81 @@
# Next.js Redis Cluster Cache Integration Example

Run docker compose to start redis locally
This example demonstrates how to integrate a Redis Cluster Cache with a Next.js application.

```
docker-compose up -d
```
## Prerequisites

open redis-1 terminal and create the cluster
- **Debian-based OS**: Ensure your operating system is Debian-based (e.g., Ubuntu, Debian).
- **Docker**: Ensure Docker is installed on your machine.
- **Node.js**: Ensure Node.js (and npm) is installed on your machine.

```
redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
```
### Windows and MacOS Users

type "yes" to apply the configuration.
MacOS and Windows users can use [Dev Containers](https://code.visualstudio.com/docs/devcontainers/containers) to run the example in a containerized environment. This project includes a `devcontainer.json` file that defines the container image and the necessary dependencies.

verify that the cluster was created
## Getting Started

```
redis-cli -c
cluster nodes
```
### Step 1: Start Redis Using Docker Compose

install next.js dependencies for the project
To start Redis locally, run:

```sh
docker-compose up -d
```

### Step 2: Install Project Dependencies

Install the necessary Next.js dependencies:

```sh
npm i
```

build next.js (will not use custom redis cache handler during build)
### Step 3: Build the Next.js Application

```
Build the Next.js application. Note that the custom Redis cache handler will not be used during the build process:

```sh
npm run build
```

start next.js app
### Step 4: Start the Next.js Application

```
Start the Next.js application:

```sh
npm run start
```

navigate to the local homepage [http://localhost:3000/cet](http://localhost:3000/cet)
### Step 5: Access the Application

Navigate to the local homepage in your browser:

http://localhost:3000

Then navigate through the different timezones to see the cache in action.

## Logging Configuration

To remove logs, edit the package.json file by removing `NEXT_PRIVATE_DEBUG_CACHE=1`.

to remove logs, remove NEXT_PRIVATE_DEBUG_CACHE=1 from package.json
## Redis Cluster Management

keep in mind that redis data will be stored in redis/node-X/data
### Redis Data Storage

to flush all the redis cluster use
Redis data will be stored in `redis/node-X/data`.

### Flushing the Redis Cluster

To flush all data from the Redis cluster, use the following commands:

Enter the Docker container:

```sh
docker exec -it cache-handler-redis-cluster-example-redis-1-1 /bin/bash
```

Flush all Redis nodes:

```sh
redis-cli --cluster call --cluster-only-masters 172.38.0.11:6379 FLUSHALL
```
```
117 changes: 117 additions & 0 deletions app/[[...timezone]]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { Suspense } from "react";
import Link from "next/link";
import { notFound } from "next/navigation";
import { CacheStateWatcher } from "../cache-state-watcher";
import { RevalidateFrom } from "../revalidate-from";

type TimeData = {
unixtime: number;
datetime: string;
timezone: string;
};

const API_BASE_URL = "https://worldtimeapi.org/api/timezone";

async function fetchTimezoneList(): Promise<string[]> {
try {
const response = await fetch(new URL(API_BASE_URL), {
next: { revalidate: false },
});

if (!response.ok) {
throw new Error("Failed to fetch timezone list.");
}

const data: string[] = await response.json();
return data;
} catch (error) {
console.error(error);
return [];
}
}

async function fetchTimezoneData(zoneName: string): Promise<TimeData | null> {
try {
const response = await fetch(new URL(zoneName, API_BASE_URL), {
next: { tags: ["time-data"] },
});

if (!response.ok) {
throw new Error("Failed to fetch timezone data.");
}

const data: TimeData = await response.json();

return {
timezone: data.timezone,
datetime: data.datetime,
unixtime: data.unixtime,
};
} catch (error) {
console.error(error);

return null;
}
}

const PRE_RENDERED_TIMEZONES = ["CET", "WET", "Africa/Abidjan"];

export const revalidate = 500;

export async function generateStaticParams() {
return PRE_RENDERED_TIMEZONES.map((timezone) => ({
timezone: timezone.split("/"),
}));
}

type PageProps = {
params: { timezone?: string[] };
};

export default async function Page({ params: { timezone: slug = [] } }: PageProps) {
const timezoneList = await fetchTimezoneList();

const currentTimezone = slug.length ? slug.join("/") : PRE_RENDERED_TIMEZONES[0];

const timeData = await fetchTimezoneData(currentTimezone);

if (!timeData) {
notFound();
}

return (
<>
<aside className="sidebar">
{timezoneList.map(timezone => (
<Link key={timezone} className="sidebar-link" href={`/${timezone}`}>
{timezone}
</Link>
))}
</aside>
<div className="main-content">
<main className="widget">
<div className="pre-rendered-at">
{timeData.timezone} Time {timeData.datetime}
</div>
<Suspense fallback={null}>
<CacheStateWatcher
revalidateAfter={revalidate * 1000}
time={timeData.unixtime * 1000}
/>
</Suspense>
<RevalidateFrom />
</main>
</div>
<footer className="footer">
<Link
href={process.env.NEXT_PUBLIC_REDIS_INSIGHT_URL ?? "http://localhost:5540"}
className="link"
target="_blank"
rel="noopener noreferrer"
>
View RedisInsight &#x21AA;
</Link>
</footer>
</>
);
}
68 changes: 0 additions & 68 deletions app/[timezone]/page.tsx

This file was deleted.

Loading

0 comments on commit 1ef74bd

Please sign in to comment.