Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created October 10, 2019 11:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bennadel/22001831bc610dc03e0bd1ea7140c5cc to your computer and use it in GitHub Desktop.
Save bennadel/22001831bc610dc03e0bd1ea7140c5cc to your computer and use it in GitHub Desktop.
Playing With Lists And Blocking Pop Operations In Redis And Lucee 5.2.9.40
component
output = false
hint = "I define the application settings and event handlers."
{
// Configure the application runtime.
this.name = "RedisListWithJsonExploration";
this.applicationTimeout = createTimeSpan( 0, 1, 0, 0 );
this.sessionManagement = false;
// Setup the mappings for our path evaluation.
this.webrootDir = getDirectoryFromPath( getCurrentTemplatePath() );
this.mappings = {
"/": this.webrootDir,
"/javaloader": "#this.webrootDir#vendor/JavaLoader/javaloader/",
"/JavaLoaderFactory": "#this.webrootDir#vendor/JavaLoaderFactory/",
"/jedis": "#this.webrootDir#vendor/jedis-2.9.3/"
};
// ---
// EVENT METHODS.
// ---
/**
* I get called once when the application is being initialized.
*/
public boolean function onApplicationStart() {
var javaLoaderFactory = new JavaLoaderFactory.JavaLoaderFactory();
application.javaLoaderForJedis = javaLoaderFactory.getJavaLoader([
expandPath( "/jedis/commons-pool2-2.4.3.jar" ),
expandPath( "/jedis/jedis-2.9.3.jar" ),
expandPath( "/jedis/slf4j-api-1.7.22.jar" )
]);
var jedisConfig = application.javaLoaderForJedis
.create( "redis.clients.jedis.JedisPoolConfig" )
.init()
;
application.jedisPool = application.javaLoaderForJedis
.create( "redis.clients.jedis.JedisPool" )
.init( jedisConfig, "127.0.0.1" )
;
return( true );
}
/**
* I get called once at the start of each request.
*/
public boolean function onRequestStart() {
request.withRedis = this.withRedis;
return( true );
}
// ---
// PUBLIC METHODS.
// ---
/**
* I invoke the given Callback with an instance of a Redis connection from the Jedis
* connection pool. The value returned by the Callback is passed-back up to the
* calling context. This removes the need to manage the connection in the calling
* context.
*
* @callback I am a Function that is invoked with an instance of a Redis connection.
*/
public any function withRedis( required function callback ) {
try {
var redis = application.jedisPool.getResource();
return( callback( redis ) );
} finally {
redis?.close();
}
}
}
<cfoutput>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>
Read a Message
</title>
</head>
<body>
<h1>
Read a Message
</h1>
<cfscript>
// For the purposes of the demo, we're going to block and listen for about
// 30-seconds until the page has to be refreshed (for more messages).
startedAt = getTickCount();
cutoffAt = ( startedAt + ( 30 * 1000 ) );
while ( getTickCount() < cutoffAt ) {
// Since the BLPOP operation is going to BLOCK the page request, let's
// flush data to the Browser incrementally so that we can consume and
// display the messages in "realtime" as they are pushed onto the list.
cfflush( interval = 1 );
results = request.withRedis(
( redis ) => {
// NOTE: If the operation doesn't receive any message in 5
// seconds, it will stop blocking and return NULL.
return( redis.blpop( 5, [ "list:messages" ] ) );
}
);
// The result of the BLPOP operation is 2-tuple where the first index is
// the name of the key (since we can block on multiple keys at the same
// time); and, the second index is the value of the list item.
if ( ! isNull( results ) ) {
dump( deserializeJson( results[ 2 ] ) );
echo( "<script>scrollTo( 0, 999999 );</script>" );
echo( "<br />" );
}
}
</cfscript>
<a href="#cgi.script_name#">Refresh listener</a>
</body>
</html>
</cfoutput>
<cfscript>
// If the form has been submitted, push a Message onto the List.
// --
// NOTE: Each list item is a String that represents a serialized object.
if ( form.keyExists( "message" ) && form.message.len() ) {
listItem = {
"id": createUUID().lcase(),
"message": form.message,
"createdAt": getTickCount()
};
request.withRedis(
( redis ) => {
redis.rpush( "list:messages", [ serializeJson( listItem ) ] );
}
);
}
</cfscript>
<cfoutput>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>
Post a Message
</title>
<link rel="stylesheet" type="text/css" href="./styles.css" />
</head>
<body>
<h1>
Post a Message
</h1>
<form method="post" action="#cgi.script_name#">
<strong>Message:</strong><br />
<input type="text" name="message" autofocus size="30" />
<button type="submit">
Post
</button>
</form>
</body>
</html>
</cfoutput>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment