ভূমিকা
ফ্রন্টএন্ড ও ব্যাকএন্ড আলাদা রাখার যুগে আমাদের জীবন অসংখ্য সুযোগ ও চ্যালেঞ্জে ভরা——হঠাৎ দেখা দেওয়া মাথা খারাপ করে দেওয়ার মতো Bug-ও তার মধ্যে পড়ে। আজ আমরা কথা বলব এমন এক চিরচেনা সমস্যা নিয়ে, যা অসংখ্য ডেভেলপারকে হাসবেন না কাঁদবেন বুঝতে দেয় না:ব্যাকএন্ড Long টাইপ ID অতিরিক্ত দীর্ঘ হওয়ায় ফ্রন্টএন্ডে সংখ্যাগত নির্ভুলতা হারিয়ে যায়। এই সমস্যার কথা উঠলে, একে সত্যিই “সমস্ত অনিষ্টের মূল” বলা চলে, কারণ JavaScript নিরাপদে শুধু প্রক্রিয়াকরণ করতে পারে Number.MAX_SAFE_INTEGER(অর্থাৎ 9007199254740991)এর মধ্যে থাকা সংখ্যাগুলোর ক্ষেত্রে কী হবে?
আপনিও যদি কখনো এই সমস্যা নিয়ে মাথা ঘামিয়ে থাকেন, চিন্তা করবেন না, আপনি একা নন। আজ আমরা রসিকতার মাধ্যমে এই “শতাব্দীর দুরূহ সমস্যা” বিশ্লেষণ করার পাশাপাশি এর গোড়া থেকে সমাধান করতে সাহায্য করব, যাতে আপনার কোড আর “নির্ভুলতা হারিয়ে নিঃসার” না হয়।
সমস্যার পটভূমি:কেন Long এত লম্বা?
প্রথমে, আমাদের শুরু করতে হবে Long ধরন থেকে আলোচনা শুরু করা যাক।Long,অর্থাৎ Java -এর 64 বিটের পূর্ণসংখ্যার ধরন,যাঁরা প্রক্রিয়াকরণ করতে পছন্দ করেন তাঁদের জন্য বিগ ডেটা、বড় সংখ্যার Java-র কাছে এই টাইপ যেন আশীর্বাদ। অথচ ফ্রন্টএন্ডের জগৎ কিছুটা “অল্পজানা”, এটি কেবল 53 বিটের মধ্যে থাকা পূর্ণসংখ্যা সামলাতে জানে। হ্যাঁ, ঠিকই শুনেছেন, এই বিষয়ে JavaScript যেন এক “আধা তাল পিছিয়ে থাকা” বৃদ্ধ পণ্ডিত, আরও বড় সংখ্যার মুখোমুখি হলে সে দশমিক বিন্দু আর সূচক নিয়ে নাড়াচাড়া করতে শুরু করে, শেষে এমন একটি সংখ্যা উগরে দেয় যা দেখে আপনি হতাশ হয়ে পড়েন। তখন নির্ভুলতা হারানোর সমস্যাটি ভূতের মতো আপনার প্রকল্পে ঘুরে বেড়াতে শুরু করে।
প্রথম সাক্ষাৎ:নির্ভুলতা হারানোর ঘটনাগুলো
গল্পটা শুরু করতে হবে একদিনের Bug প্রতিবেদনের মাধ্যমে: “ডেভেলপার দাদা, দেখুন তো, এই ID-টা এমন বদলে গেল কীভাবে? এটা তো আমার ডেটাবেস এর ভেতরের সেই ID-টাই啊!”(সেই ID-টাই তো!)আপনি ভ্রু কুঁচকে দেখে নিলেন,হ্যাঁ,9223372036854775807 পরিণত হলো 9223372036854776000,আরে, এই বাড়তি সংখ্যাগুলো যেন একেবারে জাদুর মতো হাজির হয়েছে। ব্যাকএন্ড তো ঠিক সংখ্যাই দিয়েছিল! মাথা চুলকাতে চুলকাতে আপনি ভাবছেন: “এটা নিশ্চয়ই ফ্রন্টএন্ডের দোষ!”
দোষ ফ্রন্টএন্ডের, নাকি ব্যাকএন্ডের?——মূল কারণের সন্ধানে
আসলে, এই সমস্যার দায় ফ্রন্টএন্ডের ওপর চাপানোও একেবারে অযৌক্তিক নয়। চলুন দেখি JavaScript সংখ্যা নিয়ে কাজ করার ক্ষেত্রে “দুর্বলতা”।JavaScript এর Number টাইপটি IEEE 754 মানভিত্তিক দ্বৈত নির্ভুলতার ফ্লোটিং পয়েন্ট বিন্যাস, যা নিরাপদভাবে কেবল 53 বিটের বাইনারি সংখ্যা প্রকাশ করতে পারে, অর্থাৎ Number.MAX_SAFE_INTEGER এর মান——9007199254740991。
অন্য কথায়, এই সীমার বাইরের পূর্ণসংখ্যাগুলি,JavaScript তখনই “নির্ভুলতায় ছাড়” শুরু হয়, এর “মস্তিষ্কের ধারণক্ষমতা” হঠাৎই আর কুলোয় না। ফলে, আপনার Long ধরনের ID তার চোখে ততটা “আকর্ষণীয়” নয় এমন একগুচ্ছ সংখ্যায় পরিণত হয়। ঠিক এই কারণেই,ফ্রন্টএন্ডকে অস্বাভাবিক দীর্ঘ এই ID-গুলি সামলাতে গিয়ে একটু “ত্যাগ” করতেই হয়,ফলে আপনার আসল বিশ্বস্ত ID ছেঁটে গিয়ে,একটি “মজার” নতুন সংখ্যায় পরিণত হয়।
হতবাক অবস্থা থেকে শান্ত হওয়া: সমাধানের খোঁজে
যেহেতু সমস্যার মূল খুঁজে পাওয়া গেছে, এবার আমাদের মতো ডেভেলপারদের দক্ষতা দেখানোর পালা। এরপর আমি কয়েকটি সমাধান গভীরভাবে বুঝিয়ে দেব এবং প্রতিটির সুবিধা-অসুবিধাও জানাব; শেষ পর্যন্ত সব পথই তো রোমে পৌঁছায়।
1. সরাসরি স্ট্রিংয়ে রূপান্তর:সহজ ও সরাসরি, তবু কার্যকর
এমন সমস্যা হলে প্রথমেই নিশ্চয়ই সবচেয়ে সহজ ও সরাসরি পদ্ধতিটির কথা মনে হবে: সরাসরি Long টাইপের তথ্য রূপান্তর স্ট্রিং বানিয়ে দিলেই তো হয়! যেহেতু JavaScript ফ্লোটিং-পয়েন্ট সংখ্যার অন্ধ ভক্ত হলে,আমরা বরং প্রশ্নটা তার কাছেই ফিরিয়ে দিই,তাকে বলি:“তোমাকে শুধু এটাকে একটা স্ট্রিং হিসেবে ধরতে হবে,এটা কত লম্বা তা নিয়ে চিন্তা কোরো না!”
এটি কীভাবে করা যায়?আসলে খুবই সহজ, ব্যবহার করুন Jackson প্রদত্ত ToStringSerializer,আমরা সহজেই Long স্ট্রিংয়ে রূপান্তর করা।
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
public class UserDto {
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
// 其他字段
}এভাবে, যখন ব্যাকএন্ড ফেরত দেয় UserDto যখন,Long টাইপের id স্ট্রিং হিসেবে সিরিয়ালাইজ করা হবে, ফ্রন্টএন্ডও স্ট্রিংই পাবে, ফলে নির্ভুলতার সমস্যা সহজেই মিটে যাবে। যেন JavaScript-এর মাথায় একটি চিরকুট সেঁটে দেওয়া হলো:“এটি স্ট্রিং, তোমার মাথা ঘামানোর দরকার নেই!”
2. ফ্রন্টএন্ডে ব্যবহার BigInt:বড় সংখ্যার ক্ষেত্রেও নির্ভুল গণনা সম্ভব করা
অবশ্য সরল ও সরাসরি পদ্ধতি সব পরিস্থিতিতে মানায় না। ভাবুন তো, ফ্রন্টএন্ডে যদি এই ID নিয়ে কোনো গাণিতিক হিসাব করতে হয়, তাহলে সরাসরি স্ট্রিংয়ে বদলে দেওয়া খুব ভালো হবে না। তাহলে ফ্রন্টএন্ডে এই “অতিরিক্ত দীর্ঘ” ID-গুলি কীভাবে সামলানো উচিত?
সৌভাগ্যবশত,JavaScript একেবারে অকাজেরও নয়। চালু করা হয়েছে BigInt এরপর,JavaScript অবশেষে আর সেই কেবল দশমিক বিন্দু নিয়ে নাড়াচাড়া করতে জানা বোকাটি রইল না।BigInt একটি নতুন প্রিমিটিভ ডেটা টাইপ, যা বিশেষভাবে যেকোনো নির্ভুলতার পূর্ণসংখ্যা নিয়ে কাজ করার জন্য তৈরি। আপনি এভাবে করতে পারেন:
const id = BigInt("9223372036854775807");
console.log(id + 1n); // 输出:9223372036854775808nএভাবে, আপনি ফ্রন্টএন্ডে নির্ভুলভাবে প্রক্রিয়া করতে পারবেন Long টাইপের ডেটা, যাতে নির্ভুলতা হারানোর সমস্যা এড়ানো যায়। অবশ্য, এখানে ছোট্ট একটি কথা মনে করিয়ে দেওয়া দরকার:BigInt সব ব্রাউজার এটি সমর্থন করে না, তাই আপনার অ্যাপ্লিকেশনের পরিবেশ এর সঙ্গে সামঞ্জস্যপূর্ণ কি না নিশ্চিত করতে হবে।
3. নিজস্ব সিরিয়ালাইজেশন:জটিল সমস্যাকে সহজ করা
যদিও ওপরের পদ্ধতিগুলি দিয়ে বেশিরভাগ সমস্যাই সমাধান করা যায়, কখনো কখনো এমন পরিস্থিতি আসে যেখানে আরও সূক্ষ্ম নিয়ন্ত্রণ প্রয়োজন। তখন,Jackson এর কাস্টম সিরিয়ালাইজার তখন কাজে আসে।
প্রয়োজন অনুযায়ী নমনীয়ভাবে নিয়ন্ত্রণ করতে আমরা একটি নিজস্ব সিরিয়ালাইজার লিখতে পারি Long টাইপের ফিল্ডের সিরিয়ালাইজেশন প্রক্রিয়া। যেমন, সিরিয়ালাইজ করার সময় আমরা সিদ্ধান্ত নিতে পারি যে Long স্ট্রিংয়ে রূপান্তর করুন,অথবা নির্দিষ্ট শর্ত অনুযায়ী আলাদাভাবে প্রক্রিয়াকরণ করুন:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
public class CustomLongSerializer extends JsonSerializer<Long> {
@Override
public void serialize(Long value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value != null) {
gen.writeString(value.toString());
}
}
}তারপর যেখানে প্রয়োজন, এই কাস্টম সিরিয়ালাইজারটি প্রয়োগ করুন:
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
public class UserDto {
@JsonSerialize(using = CustomLongSerializer.class)
private Long id;
// 其他字段
}এভাবে, পুরো সিরিয়ালাইজেশন প্রক্রিয়া আপনার নিয়ন্ত্রণে থাকবে, প্রতিটি Long সবই আপনার কাঙ্ক্ষিত উপায়ে প্রক্রিয়াকরণ করা যায়। অবশ্য,এই পদ্ধতি নমনীয় হলেও কিছুটা বেশি জটিল,কয়েক লাইন কোড বেশি লিখতে হয়,রক্ষণাবেক্ষণের খরচও বাড়তে পারে।
4. গ্লোবালভাবে সামলানো:সময় ও শ্রম বাঁচানোর উপায়
প্রতিটি ফিল্ডে অ্যানোটেশন কনফিগার করার ঝামেলা এড়িয়ে,সমস্যাটি সর্বত্র সমাধান করতে চাইলে,গ্লোবাল কনফিগারেশন বিবেচনা করতে পারেন ObjectMapper। এর মধ্যে Spring Boot -এর মধ্যে গ্লোবাল কনফিগার করা ObjectMapper,আপনি সবগুলোকে Long টাইপের সব ফিল্ড স্বয়ংক্রিয়ভাবে স্ট্রিংয়ে রূপান্তরিত হয়।
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(Long.class, ToStringSerializer.instance);
module.addSerializer(Long.TYPE, ToStringSerializer.instance);
mapper.registerModule(module);
return mapper;
}
}একবার করলেই স্থায়ী সমাধান, কোনো ফিল্ডে সিরিয়ালাইজার কনফিগার করতে ভুলে যাওয়ার চিন্তা নেই, পুরো প্রকল্পেই সংখ্যার নির্ভুলতা অক্ষুণ্ণ থাকার সুবিধা মিলবে। অবশ্য গ্লোবাল কনফিগারেশন ব্যবহার করার সময় খেয়াল রাখতে হবে, এটি এমন কিছুকেও প্রভাবিত করতে পারে যেগুলি আপনি রূপান্তর করতে চান না Long ফিল্ড, তাই সতর্কভাবে বিবেচনা করা প্রয়োজন।
বাস্তব উদাহরণ:নানা বাধা পেরিয়ে একটি বাগ সমাধানের পথ
এই সমাধানগুলোর বাস্তব ফলাফল সবাইকে আরও ভালোভাবে বোঝাতে, একটি বাস্তব প্রকল্পের গল্প বলি। এই প্রকল্পে ব্যবহারকারীর ID ব্যবস্থাপনার কাজ ছিল, আর ব্যবহারকারীর সংখ্যা অনেক বেশি হওয়ায় ব্যাকএন্ডে ব্যবহার করা হয়েছিল Long ধরনের অনন্য শনাক্তকারী। প্রকল্প চালু হওয়ার কয়েক দিনের মধ্যেই ব্যবহারকারীর অভিযোগ এল:“আমার ID দেখতে বদলে গেল কেন?”。
প্রথম ধাপ:সমস্যার মূল অনুসন্ধান
আমরা প্রথমে ফ্রন্টএন্ডের কোড পরীক্ষা করে দেখতে পাই JavaScript এর Number টাইপটি সত্যিই 19 অঙ্কের এই দীর্ঘ সংখ্যাটিকে নির্ভুলভাবে প্রকাশ করতে পারে না, ফলে নির্ভুলতা হারিয়ে যায়। এরপর আমরা ব্যাকএন্ডের কোড দেখে জানতে পারি, যদিও Long ধরনের ডেটা ব্যাকএন্ডে সঠিক থাকে, কিন্তু REST-এর মাধ্যমে API ফ্রন্টএন্ডে ফেরত দেওয়ার সময়,সংখ্যার নির্ভুলতা হারিয়ে গেছে。
দ্বিতীয় ধাপ: উপযুক্ত সমাধান বেছে নেওয়া
সমস্যাটি দ্রুত সমাধানের জন্য,আমরা প্রথমে ব্যবহারের সিদ্ধান্ত নিলাম @JsonSerialize(using = ToStringSerializer.class) এই সহজ ও কার্যকর পদ্ধতি। এই পদ্ধতির মাধ্যমে আমরা সফলভাবে ফ্রন্টএন্ডে নির্ভুলতা হারানো সংখ্যা পৌঁছানো ঠেকাতে পেরেছি।
তবে,সমস্যা পুরোপুরি মেটেনি। পরবর্তী চাহিদায়,ফ্রন্টএন্ডকে ID নিয়ে কিছু গণনামূলক কাজ করতে হয়,যেমন ব্যবহারকারীদের ID অনুযায়ী সাজানো। তখন স্ট্রিং কিছুটা অপ্রতুল বলে মনে হয়।
তৃতীয় ধাপ:ব্যবহার করুন BigInt ফ্রন্টএন্ডের গণনাসংক্রান্ত সমস্যার সমাধান
তাই, আমরা ফ্রন্টএন্ডে যুক্ত করার সিদ্ধান্ত নিলাম BigInt। ব্যবহার করে BigInt,ফ্রন্টএন্ড এই অতি দীর্ঘ ID-গুলো নির্ভুলভাবে সংরক্ষণ করার পাশাপাশি প্রয়োজনীয় গাণিতিক হিসাবও করতে পারে। পরীক্ষায় দেখা গেছে, প্রধান ব্রাউজারগুলোতে এই পদ্ধতি ভালো কাজ করে; একমাত্র অসুবিধা হলো, কিছু পুরোনো ব্রাউজার সংস্করণে এর সমর্থন তেমন ভালো নয়।
চতুর্থ ধাপ: চূড়ান্ত গ্লোবাল কনফিগারেশন
ভবিষ্যতে একই ধরনের সমস্যা আবার এড়াতে আমরা সিদ্ধান্ত নিয়েছি ToStringSerializer সর্বত্র কার্যকর হওয়ার জন্য কনফিগার করুন।এতে সমস্ত Long এই ধরনের ডেটা সিরিয়ালাইজেশনের সময় স্বয়ংক্রিয়ভাবে স্ট্রিংয়ে রূপান্তরিত হয়, ফলে ফ্রন্টএন্ডের ডেটার নির্ভুলতা নিশ্চিত হওয়ার পাশাপাশি কোডে একই কনফিগারেশন বারবার লেখার প্রয়োজনও কমে।
শেষকথা: নির্ভুলতা হারানোর অবসানকারী
এই উদাহরণ থেকে দেখা যায়, যদিও Long টাইপের নির্ভুলতা হারানোর সমস্যাটি দেখতে সহজ মনে হলেও, বাস্তব প্রকল্পে তা অনেক সুপ্ত সমস্যা তৈরি করতে পারে। বিভিন্ন সমাধান তুলনা ও চেষ্টা করে, আমরা শেষ পর্যন্ত নিজেদের প্রকল্পের জন্য সবচেয়ে উপযুক্ত সমাধানটি পেয়েছি।
আশা করি, এই রসিক অথচ বিস্তারিত ব্লগটি আপনাকে সামলাতে সাহায্য করবে Long ডেটা টাইপের নির্ভুলতা হারানোর সমস্যায় অপ্রয়োজনীয় ঘুরপথ এড়াতে। আপনারও এমন অভিজ্ঞতা থাকলে মন্তব্যে নিজের গল্প জানাতে পারেন; হয়তো আপনার অভিজ্ঞতা অন্য ডেভেলপারদের আরও অনুপ্রেরণা দেবে।
শেষে মনে রাখবেন, কোডও জীবনের মতো, মাঝেমধ্যে কিছু “হারিয়ে যাওয়া” ভয়ের নয়, আসল কথা হলো উপযুক্ত “সিরিয়ালাইজার” খুঁজে তাকে সঠিক পথে ফিরিয়ে আনা। সবার কোড যেন আর কখনো “নির্ভুলতা না হারায়”, যথাযথ থাকে এবং নির্বিঘ্নে চলতে থাকে!