global_session
Version:
A link between LRU and messaging to peers which may store or cache LRU entries remotely
508 lines (409 loc) • 14.3 kB
JavaScript
const {MessageRelayer} = require('message-relay-services')
const {make_process_identifier,remove_special_files} = require('../lib/utils')
const LRU = require('shm-lru-cache')
const {fix_path,conf_loader} = require('../lib/utils')
let conf_path = process.argv[2]
let default_path = './test/mid-client.conf'
let test_conf = conf_loader(conf_path,default_path)
let g_test_list = []
if ( test_conf === false ) {
console.log("NO configuration")
process.exit(1)
}
process.on('SIGINT',() => {
remove_special_files()
process.exit(0)
})
// A client of a global session endpoint
class FAUX_LRU extends LRU {
constructor(conf) {
super(conf)
console.log("FAUX_LRU")
console.dir(conf)
this.count = 253
this.proc_index = -1
}
}
// ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
const DFLT_SLOW_MESSAGE_QUERY_INTERVAL = 5000
const DFLT_FAST_MESSAGE_QUERY_INTERVAL = 1000
// ---- ---- ---- ---- ---- ---- ---- ---- ----
const SESSION_GENERATION_PUB = "session"
const WANT_SESSION_FOR_ID = "need_session"
const APPRISE_SESSION_IN_RESPONSE = "session_known"
const SESSION_ALREADY_RECEIVED = "session_received"
class FAUX_FOR_TEST_LRUManager {
//
constructor(conf) {
//
if ( conf.module_path !== undefined ) {
conf.module_path = conf.module_path.replace('/lib','')
conf.module_path = fix_path(conf.module_path)
conf.cache.module_path = conf.module_path
}
if ( conf.token_path !== undefined && conf.cache.token_path === undefined ) {
conf.cache.token_path = conf.token_path
}
//
conf.cache._test_use_no_memory = true /// LINE ADDED FOR TEST
//
this.cache = new FAUX_LRU(conf.cache)
this.in_operation = true
this.conf = conf
//
}
initialize(conf) {
}
pure_hash(key) {
return this.cache.pure_hash(key)
}
augment_hash(hash) {
return this.cache.augment_hash(hash)
}
//
set(key,value) {
let sdata = ( typeof value !== 'string' ) ? JSON.stringify(value) : value
let augmented_hash_token = this.cache.hash(key)
this.cache.set(augmented_hash_token, sdata) // store in the LRU cache
return(augmented_hash_token) // return the derived key
}
set_with_token(token,value) {
let sdata = ( typeof value !== 'string' ) ? JSON.stringify(value) : value
this.cache.set(token, sdata) // store in the LRU cache
}
hash_set(key,value) {
let sdata = ( typeof value !== 'string' ) ? JSON.stringify(value) : value
let augmented_hash_token = this.cache.hash(key)
let hh_unidentified = this.cache.hasher(sdata)
this.cache.set(augmented_hash_token, hh_unidentified) // store in the LRU cache
return(hh_unidentified) // return the derived key
}
check_hash(hh_unid,value) {
let sdata = ( typeof value !== 'string' ) ? JSON.stringify(value) : value
let hh_unidentified = this.cache.hasher(sdata)
return hh_unidentified === hh_unid
}
//
delete(key) { // token must have been returned by set () -> augmented_hash_token
let augmented_hash_token = this.cache.hash(key)
this.cache.del(augmented_hash_token)
}
//
get(key) { // token must have been returned by set () -> augmented_hash_token
let augmented_hash_token = this.cache.hash(key)
let value = false
if ( typeof value !== 'string' ) {
return false
}
return value
}
get_with_token(token) {
let value = this.cache.get(token)
if ( typeof value !== 'string' ) {
return false
}
return value
}
//
disconnect(opt) {
this.in_operation = false
return this.cache.disconnect(opt)
}
}
/*
{
"lastAccess": 1343846924959,
"cookie": {
"originalMaxAge": 172800000,
"expires": "2012-08-03T18:48:45.144Z",
"httpOnly": true,
"path": "/"
},
"user": {
"name":"waylon",
"status":"pro"
}
}
*/
// ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
const SUGGESTED_TIMEOUT = 1000*60*120 /// about 2 hours
// ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
class SessionCacheManager {
//
constructor(lru_conf,message_relays) {
//
this.conf = message_relays
this.process_identifier = make_process_identifier()
//
//
this._LRUManager = new FAUX_FOR_TEST_LRUManager(lru_conf);
this.message_fowarding = (message_relays !== undefined) ? new MessageRelayer(message_relays) : false
this._all_senders = []
//
this.section_manager = lru_conf.manage_section
if ( (lru_conf.manage_section !== false) && (this.message_fowarding !== false) ) {
let self = this
this.message_fowarding.on('client-ready',(address,port) => {
self.setup_auth_path_subscription()
})
}
//
this._want_keyed = {}
this._want_key_handlers = {}
}
// ---- ---- ---- ---- ---- ---- ---- ----
post_message(msgObject) {
if ( this.message_fowarding ) {
if ( msgObject.m_path === undefined ) {
msgObject.m_path = SESSION_GENERATION_PUB
}
this.message_fowarding.sendMessage(msgObject)
}
}
// ---- ---- ---- ---- ---- ---- ---- ----
mark_session_sent(proc_id,key) {
}
session_sent(proc_id,key) {
return false
}
application_set_key_notify(key,handler) {
this._want_key_handlers[key] = handler
}
application_notify_value(key){
let handler = this._want_key_handlers[key]
if ( typeof handler === "function" ) {
handler(key)
}
}
want_key_trace(key) {
this._want_keyed[key] = Date.now()
}
still_want_session(token) {
let now = Date.now()
let requested_when = this._want_keyed[token]
if ( requested_when === undefined ) false
if ( (now - requested_when) < SUGGESTED_TIMEOUT ) {
this.application_notify_value(token)
return true
}
delete this._want_keyed[token]
return false
}
// ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
async get(key) { // token must have been returned by set () -> augmented_hash_token
let lru_m = this._LRUManager
let value = lru_m.get(key) // test no async
//
if ( !value && this.message_fowarding ) {
this.want_key_trace(key)
let hash = lru_m.pure_hash(key)
let message = {
"hash" : hash,
"requester" : this.process_identifier
}
//
console.log("publishing",message)
let resp = await this.message_fowarding.publish(WANT_SESSION_FOR_ID,this.conf.auth_path,message)
console.log(`get(${key})`)
console.dir(resp)
if ( resp ) {
if ( resp.hash === hash ) {
let value = resp.value
let hash = resp.hash // store in local cash
let token = lru_m.augment_hash(hash)
if ( this.still_want_session(token) ) {
lru_m.set_with_token(token,value)
}
return [token,value]
}
}
return [false,value]
}
return [false,value]
}
// set in local cache and then publish (a publish -- not a send)
async set(key,value) {
let augmented_hash_token = this._LRUManager.set(key,value) // in local hash
//
// TEST LINE
g_test_list.push([augmented_hash_token, key,value])
//
if ( this.section_manager && this.message_fowarding ) {
//
let hash = augmented_hash_token.split('-')[1]
let message = {
"key" : key,
"value" : value,
"hash" : hash // this is a pure hash
}
// Tell all subscribers that a new session has been created (remote the session information)
/*
let test_respo = await this.message_fowarding.publish(SESSION_GENERATION_PUB,this.conf.auth_path,message)
console.log("set publish(SESSION_GENERATION_PUB result:")
console.dir(test_respo)
*/
this.message_fowarding.publish(SESSION_GENERATION_PUB,this.conf.auth_path,message)
return augmented_hash_token
}
}
check_hash(hh_unid,value) {
return this._LRUManager.check_hash(hh_unid,value)
}
async setup_auth_path_subscription() {
if ( this.message_fowarding === false ) return
console.log("setup_auth_path_subscription :: ")
let message = {}
let lru_m = this._LRUManager
let self = this
let proc_id = this.process_identifier
// All clients may publish a generation of ID, although usually just one machine will actively do so.
// Still all will respond.
let handler = (msg) => { // If receiving news, just set it locally using the generated hash
let value = msg.value
let token = msg.hash
lru_m.set_with_token(token,value)
}
let resp = await this.message_fowarding.subscribe(SESSION_GENERATION_PUB,this.conf.auth_path,message,handler)
console.dir(resp)
//
//
message = {}
handler = (msg) => {
let key = msg.key
if ( this.session_sent(proc_id,key) ) return
let value = self._LRUManager.get(key) // Get this session from local cache if it is there
if ( value !== false ) {
msg.value = value // this is capable of responding so send it
let proc_id = msg.requester
let targeted_topic = `${APPRISE_SESSION_IN_RESPONSE}-${proc_id}`
console.log("publishing topic" + targeted_topic)
self.message_fowarding.publish(targeted_topic,this.conf.auth_path,message)
}
}
resp = await this.message_fowarding.subscribe(WANT_SESSION_FOR_ID,this.conf.auth_path,message,handler)
console.dir(resp)
//
//
message = {}
handler = (msg) => {
//let key = msg.key
let value = msg.value
let hash = msg.hash
let token = lru_m.augment_hash(hash)
console.log(APPRISE_SESSION_IN_RESPONSE + ` got a response from pressure relief ${token} ${value}`)
if ( self.still_want_session(token) ) {
lru_m.set_with_token(token,value)
}
}
let targeted_path = `${APPRISE_SESSION_IN_RESPONSE}-${proc_id}`
console.log(`subscribing to ${targeted_path}`)
resp = await this.message_fowarding.subscribe(targeted_path,this.conf.auth_path,message,handler)
console.dir(resp)
//
//
message = {}
handler = (msg) => {
let key = msg.key
let proc_id = msg.requester
self.mark_session_sent(proc_id,key)
}
resp = await this.message_fowarding.subscribe(SESSION_ALREADY_RECEIVED,this.conf.auth_path,message,handler)
console.dir(resp)
}
add_message_handler(m_handler,q_holder,prf_slow,prf_fast) {
if ( m_handler === undefined ) return;
let handler = m_handler
if ( q_holder === undefined ) return;
let _q = q_holder
let slow = DFLT_SLOW_MESSAGE_QUERY_INTERVAL
if ( prf_slow !== undefined ) slow = prf_slow;
let fast = DFLT_FAST_MESSAGE_QUERY_INTERVAL
if ( prf_slow !== undefined ) fast = prf_fast;
let sender_index = this._all_senders.length
let message_sender = async () => {
let m_snder = this._all_senders[sender_index]
if ( m_snder ) {
if ( _q.empty_queue() ) {
setTimeout(() => { m_snder() }, slow )
} else {
//
while ( !(_q.empty_queue()) ) {
let datum = _q.get_work()
let msgObject = handler(datum)
await this.post_message(msgObject) // message to admin
}
setTimeout(() => { m_snder() }, fast )
}
}
}
this._all_senders.push(message_sender)
setTimeout(message_sender,slow)
}
}
function gen_next_kv_pair() {
let r = 0
while ( r === 0 ) {
r = Math.random()
}
let k = '' + Math.trunc(r*1000)
let v = "{some information about someone}"
return [k,v]
}
async function run_test() {
console.log("STARTING TEST")
let cache_manager = new SessionCacheManager(test_conf.lru,test_conf.message_relay)
cache_manager.message_fowarding.on('client-ready',async (address,port) => {
console.log("client-ready",address,port)
console.log("sending a message sooner: " + cache_manager.conf.auth_path)
let resp = await cache_manager.message_fowarding.send_op_on_path({
"_exec_op" : "test-from client ready"
},cache_manager.conf.auth_path,'C')
console.log('client-ready response',resp)
})
let p = new Promise((resolve,reject) => {
setTimeout(async () => {
console.log("sending a message: " + cache_manager.conf.auth_path)
let resp = await cache_manager.message_fowarding.send_op_on_path({
"_exec_op" : "test-this is what it is"
},cache_manager.conf.auth_path,'C')
console.log(resp)
resolve(true)
},2000
)})
await p
// ----
let n = parseInt(test_conf.max_messages)
console.log(`sending ${n} messages`)
for ( let i = 0; i < n; i++ ) {
let [key,value] = gen_next_kv_pair()
let aught = await cache_manager.set(key,value)
console.log(`key ${key} .. i ${i} aht: ${aught}`)
}
console.log(`sent ${n} messages`)
for ( let i = 0; i < test_conf.test_query_count; i++ ) {
let [hash,key,value] = g_test_list.shift()
console.log("getting: ",key)
let [restored_hash,restored_value] = await cache_manager.get(key)
//
console.log(`result of get ${key}`)
console.log(restored_hash)
console.log(restored_value)
// ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
//check(hash,restored_hash)
//check(value,restored_value)
}
let tst_key = '79131'
let tst_value = "{ we are testing our limits }"
let p2 = new Promise((resolve,reject) => {
setTimeout(async () => {
resolve(true)
},2000
)})
await p2
let [restored_hash2,restored_value2] = await cache_manager.get(tst_key)
//
console.log(`result of get ${tst_key}`)
console.log(restored_hash2)
console.log(restored_value2)
}
run_test()