আপনি শুনেছেন যে Redis-এ একটি এম্বেডেড স্ক্রিপ্টিং ল্যাঙ্গুয়েজ আছে, কিন্তু এখনো ট্রাই করেননি? এখানে আপনার Redis সার্ভারের সাথে Lua-এর পাওয়ার ব্যবহার করার জন্য যা জানতে হবে তার একটি ট্যুর।
Hello, Lua!
আমাদের প্রথম Redis Lua স্ক্রিপ্ট শুধু একটি ভ্যালু রিটার্ন করে আসলে Redis-এ কোনো মেনিংফুল ইন্টারঅ্যাকশন ছাড়া:
local msg = "Hello, world!" return msg এটি শুধু সিম্পল। প্রথম লাইন আমাদের মেসেজের সাথে একটি লোকাল ভেরিয়েবল সেট আপ করে, এবং দ্বিতীয় লাইন সেই ভ্যালুটা Redis সার্ভার থেকে ক্লায়েন্টের কাছে রিটার্ন করে। এই ফাইলটি লোকালি hello.lua হিসেবে সেভ করুন এবং এইভাবে রান করুন:
redis-cli --eval hello.lua কানেক্টিভিটি সমস্যা?
এই redis-cli উদাহরণ অনুমান করে যে আপনি লোকালি একটি Redis সার্ভার রান করছেন। আপনি যদি RedisGreen-এর মত একটি রিমোট সার্ভারের সাথে কাজ করেন, তবে আপনাকে হোস্ট এবং পোর্ট ইনফরমেশন স্পেসিফাই করতে হবে। আপনার RedisGreen ড্যাশবোর্ডের Connect বাটন খুঁজে বের করুন আপনার সার্ভারের লগইন ইনফো দ্রুত কপি করতে।
আরও দেখুন: Could not connect to Redis at 127.0.0.1:6379: Connection refused.
এটি রান করলে "Hello, world!" প্রিন্ট হবে। EVAL-এর প্রথম আর্গুমেন্ট হলো কমপ্লিট lua স্ক্রিপ্ট - এখানে আমরা cat কমান্ড ব্যবহার করছি ফাইল থেকে স্ক্রিপ্ট পড়তে। দ্বিতীয় আর্গুমেন্ট হলো Redis কীগুলোর সংখ্যা যা স্ক্রিপ্ট এক্সেস করবে। আমাদের সিম্পল "Hello World" স্ক্রিপ্ট কোনো কী এক্সেস করে না, তাই আমরা 0 ব্যবহার করি।
কী এবং আর্গুমেন্ট এক্সেস করা
মান লও আমরা একটি URL-শর্টেনার বিল্ড করছি। প্রতিবার একটি URL আসলে আমরা সেটি স্টোর করতে চাই এবং একটি উনিক নম্বর রিটার্ন করতে চাই যেটি পরে URL অ্যাক্সেস করতে ব্যবহার করা যাবে।
আমরা একটি Lua স্ক্রিপ্ট ব্যবহার করব Redis থেকে একটি উনিক ID পেতে INCR ব্যবহার করে এবং azonই URLটি একটি হ্যাশে স্টোর করব যা উনিক ID-দ্বারা কীড হবে:
local link_id = redis.call("INCR", KEYS[1]) redis.call("HSET", KEYS[2], link_id, ARGV[1]) return link_id আমরা এখানে প্রথমবার Redis এক্সেস করছি, call() ফাংশন ব্যবহার করে। call()-এর আর্গুমেন্টগুলো হলো Redis-এ পাঠানোর কমান্ডগুলো: প্রথমে INCR <key>, তারপর HSET <key> <field> <value>। এই দুটি কমান্ড সিকোয়েন্সিয়ালি রান হবে - Redis এই স্ক্রিপ্ট এক্সিকিউট করার সময় কিছুই করবে না, এবং এটি খুব দ্রুত রান হবে।
আমরা দুটি Lua টেবিল এক্সেস করছি, KEYS এবং ARGV। টেবিলগুলো অ্যাসোসিয়েটিভ অ্যারে, এবং Lua-র ডেটা স্ট্রাকচার করার একমাত্র মেকানিজম। আমাদের উদ্দেশ্যে আপনি সেগুলো আপনার কমফরটেবল ল্যাঙ্গুয়েজের অ্যারের সমতুল্য মনে করতে পারেন, কিন্তু এই দুটি Lua-ইজম নোট করুন যা নতুনদের ফাঁসিয়ে ফেলে:
-
টেবিলগুলো ওন-বেসড (one-based), অর্থাৎ ইন্ডেক্সিং ১ থেকে শুরু হয়। তাই
mytable-এর প্রথম এলিমেন্টmytable[1], দ্বিতীয়mytable[2], ইত্যাদি। -
টেবিলগুলো nil ভ্যালু ধারণ করতে পারে না। যদি কোনো অপারেশন
[ 1, nil, 3, 4 ]এর মত টেবিল দেয়, তবে ফলাফল হবে[ 1 ]- টেবিলটি ট্রাঙ্কেট হয়ে যাবে প্রথম nil ভ্যালুর সময়।
যখন আমরা এই স্ক্রিপ্টটি ইনভোক করব, তবে আমাদের KEYS এবং ARGV টেবিলের ভ্যালুগুলিও পাস করতে হবে। র أنسপ্রি Redis প্রোটোকলে, কমান্ডটি দেখতে যেন:
EVAL $incrset.lua 2 links:counter links:url https://malcolmgladwellbookgenerator.com/ EVAL কল করার সময়, স্ক্রিপ্টের পর আমরা 2 প্রোভাইড করি KEYS সংখ্যা হিসেবে যেগুলো এক্সেস হবে, তারপর আমাদের KEYS লিস্ট করি, এবং শেষে ARGV-এর জন্য ভ্যালু প্রোভাইড করি।
সাধারণত যখন আমরা Redis Lua স্ক্রিপ্ট দিয়ে অ্যাপ বিল্ড করি, Redis ক্লায়েন্ট লাইব্রেরি কী সংখ্যা স্পেসিফাই করার যত্ন নেয়। উপরের কোড ব্লক কমপ্লিটেনেসের জন্য দেখানো হয়েছে, কিন্তু কমান্ড লাইনে এটি করার সহজ পদ্ধতি:
redis-cli --eval incrset.lua links:counter links:urls , https://malcolmgladwellbookgenerator.com/ --eval ব্যবহার করার সময়, কমা KEYS[] কে ARGV[] আইটেমগুলো থেকে আলাদা করে।
সব কিছু স্পষ্ট করতে, এখানে আমাদের অরিজিনাল স্ক্রিপ্ট আবার, এইবার KEYS এবং ARGV এক্সপ্যান্ডেড:
local link_id = redis.call("INCR", "links:counter") redis.call("HSET", "links:urls", link_id, "https://malcolmgladwellbookgenerator.com") return link_id Redis-এ Lua স্ক্রিপ্ট লিখতে সময়, এক্সেস করা সব কী শুধু KEYS টেবিল দিয়ে এক্সেস করা উচিত। ARGV টেবিল প্যারামিটার পাসিংয়ের জন্য ব্যবহৃত হয় - এখানে এটি সেই URL-এর ভ্যালু যেটি আমরা স্টোর করতে চাই।
কন্ডিশনাল লজিক: increx এবং hincrex
আমাদের উপরের উদাহরণ আমাদের URL-শর্টেনারের লিংক সেভ করে, কিন্তু আমাদের একটি URL কতবার অ্যাক্সেস হয়েছে সেটিও ট্র্যাক করতে হবে। সেটির জন্য আমরা Redis-এ একটি হ্যাশে একটি কাউন্টার রাখব। যখন একটি ইউজার একটি লিংক আইডেন্টিফায়ারের সাথে আসবে, আমরা চেক করব সেটি আছে কিনা, এবং থাকলে সেটির কাউন্টার ইনক্রিমেন্ট করব:
if redis.call("HEXISTS", KEYS[1], ARGV[1]) == 1 then return redis.call("HINCRBY", KEYS[1], ARGV[1], 1) else return nil end প্রতিবার কেউ একটি শর্টলিংকে ক্লিক করলে, আমরা এই স্ক্রিপ্ট রান করব যাতে লিংকটি আবার শেয়ার হয়েছে তা ট্র্যাক করা যায়। আমরা EVAL ব্যবহার করে স্ক্রিপ্টটি ইনভোক করব এবং links:visits পাস করব আমাদের সিঙ্গল কী হিসেবে এবং আগের স্ক্রিপ্ট থেকে রিটার্ন হওয়া লিংক আইডেন্টিফায়ারকে সিঙ্গল আর্গুমেন্ট হিসেবে।
স্ক্রিপ্টটি হ্যাশ ছাড়া প্রায় একই দেখতে পাবে। এখানে একটি স্ক্রিপ্ট যা একটি স্ট্যান্ডার্ড Redis কী ইনক্রিমেন্ট করে শুধু তখন যদি সেটি এক্সিস্ট করে:
if redis.call("EXISTS",KEYS[1]) == 1 then return redis.call("INCR",KEYS[1]) else return nil end SCRIPT LOAD এবং EVALSHA
মনে রাখবেন যে যখন Redis একটি Lua স্ক্রিপ্ট রান করছে, এটি কিছুই করবে না। সেরা স্ক্রিপ্টগুলো Redis-এর ছোট আটোমিক ডেটা অপারেশন ভোক্যাবুলারিকে এক্সটেন্ড করে ছোট্ট লজিক বিট দিয়ে। Lua স্ক্রিপ্টে বাগ পুরো Redis সার্ভার লক আপ করে দিতে পারে - সেটা শорт এবং ইজি-টু-ডিবাগ রাখা ভালো।
যদিও সেগুলো সাধারণত খুব ছোট, আমাদের প্রতিবার ফুল Lua স্ক্রিপ্ট স্পেসিফাই করতে হয় না। একটি রিয়েল অ্যাপ্লিকেশনে আপনি বরং আপনার প্রতিটি Lua স্ক্রিপ্ট Redis-এ রেজিস্টার করবেন যখন আপনার অ্যাপ্লিকেশন বুট হবে (অথবা ডিপ্লয় হবে), তারপর স্ক্রিপ্টগুলো পরে তাদের উনিক SHA-1 আইডেন্টিফায়ার দিয়ে কল করবেন।
redis-cli SCRIPT LOAD "return 'hello world'" # "5332031c6b470dc5a0dd9b4bf2030dea6d65de91" redis-cli EVALSHA 5332031c6b470dc5a0dd9b4bf2030dea6d65de91 0 # "hello world" এক্সপ্লিসিট SCRIPT LOAD কল সাধারণত একটি লাইভ অ্যাপ্লিকেশনে অপরিহার্য নয় কারণ EVAL ইমপ্লিসিটলি স্ক্রিপ্ট লোড করে যা পাস করা হয়। একটি অ্যাপ্লিকেশন অপ্টিমিস্টিক্যালি EVALSHA অটেম্প্ট করতে পারে এবং শুধু স্ক্রিপ্ট না পাওয়া গেলে EVAL-এ ফলব্যাক করতে পারে।
আপনি যদি Ruby প্রোগ্রামার হন, তবে Shopify-এর Wolverine দেখুন, যা Ruby অ্যাপের জন্য Lua স্ক্রিপ্ট লোড ও স্টোর সিম্পলিফাই করে। PHP প্রোগ্রামারদের জন্য, Predis Lua স্ক্রিপ্ট অ্যাড করতে সাপোর্ট করে যেন সেগুলো নরমাল Redis কমান্ডর মত কল করা যায়। আপনি যদি এই বা অন্যান্য টুল ব্যবহার করেন Lua-র সাথে ইন্টারঅ্যাকশন স্ট্যান্ডার্ডাইজ করার জন্য, আমাকে জানান - আমি জানতে চাইব কী আরও আছে।
কখন Lua ব্যবহার করবেন?
Lua-র জন্য Redis সাপোর্ট কিছুটা ওভারল্যাপ করে WATCH/MULTI/EXEC ব্লকের সাথে, যা অপারেশনগুলো গ্রুপ করে যাতে সেগুলো একসাথে এক্সিকিউট হয়। তাহলে আপনি কীভাবে একটাকে অন্যটার ওপর বেছে নেবেন? MULTI ব্লকের প্রতিটি অপারেশন ইন্ডিপেনডেন্ট হতে হবে, কিন্তু Lua-তে পরবর্তী অপারেশনগুলো পূর্ববর্তী অপারেশনের রেজাল্টের ওপর ডিপেন্ড করতে পারে। Lua স্ক্রিপ্ট ব্যবহার করলে রেস কন্ডিশনও এড়ানো যায় যা WATCH ব্যবহার করলে স্লো ক্লায়েন্টদের স্টারভ করতে পারে।
RedisGreen-এ আমরা যা দেখেছি, অধিকাংশ Lua ব্যবহার করে অ্যাপ MULTI/EXECও ব্যবহার করে, কিন্তু বিপরীতটি নয়। অধিকাংশ সফল Lua স্ক্রিপ্ট ছোট, এবং শুধু একটি ফিচার ইমপ্লিমেন্ট করে যা আপনার অ্যাপকে প্রয়োজন কিন্তু Redis ভোক্যাবুলারির অংশ নয়।
লাইব্রেরি ভিজিট করা
Redis Lua ইন্টারপ্রেটার সাতটি লাইব্রেরি লোড করে: base, table, string, math, debug, cjson, এবং cmsgpack। প্রথম কয়েকটি স্ট্যান্ডার্ড লাইব্রেরি যা আপনাকে যেকোনো ল্যাঙ্গুয়েজ থেকে আশা করা যাবে বেসিক অপারেশন করতে দেয়। После দুটি Redis-কে JSON এবং MessagePack বুঝতে দেয় - এটি একটি এক্সট্রিমলি ইউজফুল ফিচার, এবং আমি دائمًا আশ্চর্য করি কেন এটি বেশি ব্যবহার হয় না।
পাবলিক API-वाले ওয়েব অ্যাপগুলোতে JSON সব জায়গায় পড়ে থাকে। তাই হয়তো আপনার নরমাল Redis কীতে.JSON ব্লবগুলো পড়ে আছে এবং আপনি সেগুলোর ভেতর থেকে কিছু নির্দিষ্ট ভ্যালু অ্যাক্সেস করতে চান যেন আপনি সেগুলো হ্যাশ হিসেবে স্টোর کرده থাকেন। Redis JSON সাপোর্ট দিয়ে, এটা সহজ:
if redis.call("EXISTS", KEYS[1]) == 1 then local payload = redis.call("GET", KEYS[1]) return cjson.decode(payload)[ARGV[1]] else return nil end এখানে আমরা চেক করি কী এক্সিস্ট করে কিনা এবং না থাকলে দ্রুত nil রিটার্ন করি। তারপর Redis থেকে JSON ভ্যালু বের করি, cjson.decode()-দিয়ে পার্স করি, এবং রিকোয়েস্টেড ভ্যালু রিটার্ন করি।
redis-cli set apple '{ "color": "red", "type": "fruit" }' # OK redis-cli --eval json-get.lua apple , type # "fruit" এই স্ক্রিপ্ট আপনার Redis সার্ভারে লোড করলে আপনি Redis-এ স্টোরড JSON ভ্যালুগুলোর সাথে যেন সেগুলো হ্যাশ হয় এমনভাবে বিলাস করতে পারবেন। আপনার অবজেক্টগুলো যদি রিজনেবলি ছোট হয়, তবে এটি আসলে কাফি ফাস্ট, এমনকি আমাদের প্রতিবার ভ্যালু পার্স করতে হয়।
আপনি যদি একটি ইন্টারনাল API-র জন্য কাজ করেন যা পারফরমেন্স ডিমান্ড করে, তবে আপনি সম্ভবত JSON-এর বদলে MessagePack বেছে নেবেন, কারণ এটি ছোট এবং ফাস্ট। خوشকিসমتی से Redis-এ (যেমন অধিকাংশ জায়গায়), MessagePack প্রায় JSON-এর ড্রপ-ইন রিপ্লেসমেন্ট।
if redis.call("EXISTS", KEYS[1]) == 1 then local payload = redis.call("GET", KEYS[1]) return cmsgpack.unpack(payload)[ARGV[1]] else return nil end নাম্বার ক্রাঞ্চিং
Lua এবং Redis-এর ভিন্ন টাইপ সিস্টেম আছে, তাই এটি বুঝতে গুরুত্বপূর্ণ যে ভ্যালুগুলো কীভাবে পরিবর্তন হতে পারে Redis-Lua বর্ডার পার করার সময়। যখন একটি নাম্বার Lua থেকে Redis ক্লায়েন্টের কাছে আসে, এটি ইন্টিজার হয়ে যায় - ডেসিমাল পয়েন্টের পরে যেগুলো আছে সেগুলো ড্রপ হয়ে যায়:
local indiana_pi = 3.2 return indiana_pi আপনি এই স্ক্রিপ্ট রান করলে, Redis ইন্টিজার ৩ রিটার্ন করবে - পাই-এর আকর্ষণীয় অংশগুলো হারিয়ে যাবে। দেখতে সিম্পল মনে হয়, কিন্তু জিনিসগুলো কিছুটা ট্রিকি হয়ে যায় যখন আপনি স্ক্রিপ্টের মাঝপথে Redis-এ ইন্টারঅ্যাক্ট करना শুরু করেন। একটি উদাহরণ:
local indiana_pi = 3.2 redis.call("SET", "pi", indiana_pi) return redis.call("GET", "pi") এখানে ফলাফল ভ্যালুটি একটি স্ট্রিং: "3.2" কেন? Redis-এ কোনো ডেডিকেটেড न्यूमেরিক টাইপ নেই। যখন আমরা প্রথমে SET করি ভ্যালুটি, Redis সেটি স্ট্রিং হিসেবে সেভ করে, লুয়া যেন শুরুতে ভ্যালুটি ফ্লোট মনে করত। যখন আমরা পরে ভ্যালুটি বের করি, এখনও সেটি একটি স্ট্রিং।
GET/SET দিয়ে এক্সেস করা Redis-এ ভ্যালুগুলো স্ট্রিং হিসেবে চিন্তা করা উচিত সिवায় যখন INCR এবং DECR এর মত নিউমেরিক অপারেশন সেগুলোর বিরুদ্ধে রান করা হয়। এই স্পেশাল নিউমেরিক অপারেশনগুলো আসলেই ইন্টিজার রিপ্লাই রিটার্ন করে (এবং স্টোরড ভ্যালু ম্যাথেমেটিক্যাল রুল অনুযায়ী ম্যানিপুলেট করে), কিন্তু Redis-এ স্টোরড ভ্যালুর "টাইপ" এখনও একটি স্ট্রিং ভ্যালু।
গতচা: একটি সারাংশ
এই হলো Lua Redis-এ কাজ করার সময় আমরা যা সবচেয়ে কমন এররগুলো দেখি:
-
Lua-তে টেবিলগুলো ওন-বেসড, অধিকাংশ জনপ্রিয় ল্যাঙ্গুয়েজের বিপরীতে। KEYS টেবিলের প্রথম এলিমেন্ট
KEYS[1], দ্বিতীয়KEYS[2], ইত্যাদি। -
Lua-তে একটি nil ভ্যালু একটি টেবিল টার্মিনেট করে। তাই
[ 1, 2, nil, 3 ]অটোমেটিক্যালি[1, 2]হয়ে যাবে। টেবিলে nil ভ্যালু ব্যবহার করবেন না। -
redis.callএক্সেপশন-স্টাইল Lua এরর থ্রো করে, অন্যদিকেredis.pcallযেকোনো এরর অটোমেটিক্যালি ট্র্যাপ করে এবং সেগুলো টেবিল হিসেবে রিটার্ন করে যেগুলো ইন্সপেক্ট করা যায়। -
Lua নাম্বারগুলো ইন্টিজার কনভার্ট হয়ে যায় যখন Redis-এ পাঠানো হয় - ডেসিমাল পয়েন্টের পরে সব কিছু হারিয়ে যায়। ফ্লোটিং পয়েন্ট নাম্বারগুলো রিটার্ন করার আগে স্ট্রিংে কনভার্ট করুন।
-
নিশ্চিত করুন যে আপনার Lua স্ক্রিপ্টে ব্যবহৃত সব কী
KEYSটেবিলে স্পেসিফাই করা আছে, অন্যথায় আপনার স্ক্রিপ্টগুলো ভবিষ্যতে Redis ভার্সনগুলোতে ব্রেক হতে পারে। -
Lua স্ক্রিপ্টগুলো Redis-এ যেকোনো অপর অপারেশনের মত: স্ক্রিপ্ট এক্সিকিউট হলে কিছুই রান করে না। স্ক্রিপ্টগুলোকে Redis সার্ভারের ভোক্যাবুলারি এক্সপ্যান্ড করার একটি উপায় হিসেবে চিন্তা করুন - সেগুলো শর্ট এবং টু-দ-পয়েন্ট রাখুন।
আরও পড়ুন
Lua এবং Redis-র জন্য অনলাইনে অনেক গরেট রিসোর্স আছে - এখানে কয়েকটি যা আমি ব্যবহার করি:
- EVAL ডকুমেন্টেশন
- RedisGreen-এর Lua স্ক্রিপ্ট লাইব্রেরি
- Lua রেফারেন্স ম্যানুয়াল
- Lua টিউটোরিয়াল ডিরেক্টরি