Vâng, bạn cũng có thể có một hiệu bánh ở góc phố dựa vào ECM với một hàng đợi khách hàng có trật tự! Để mua bánh cần lấy một số thứ tự. Bài viết này bàn về các kỹ thuật triển khai thực hiện để nhận các số thứ tự tin cậy duy nhất từ một kho lưu trữ IBM® FileNet® P8. Một số trong các cách tiếp cận hiển nhiên đã ẩn giấu các mối nguy hiểm, nhưng có một cách tiếp cận đúng và có ích là đơn giản và hiệu năng. Cùng với cách giải quyết vấn đề chung này, chúng ta sẽ thấy một số vấn đề về phát triển P8 có quy mô lớn hơn nhiều.
25 trang |
Chia sẻ: haohao89 | Lượt xem: 1870 | Lượt tải: 1
Bạn đang xem trước 20 trang tài liệu Viết mã thú vị với các API FileNet P8 của IBM: Lấy một số, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
Viết mã thú vị với các API FileNet P8 của IBM, Phần 3: Lấy một số
Triển khai thực hiện một bộ phân phối số thứ tự trong FileNet P8
Bill Carpenter, Kiến trúc sư phần mềm ECM, IBM
Tóm tắt: Vâng, bạn cũng có thể có một hiệu bánh ở góc phố dựa vào ECM với
một hàng đợi khách hàng có trật tự! Để mua bánh cần lấy một số thứ tự. Bài viết
này bàn về các kỹ thuật triển khai thực hiện để nhận các số thứ tự tin cậy duy nhất
từ một kho lưu trữ IBM® FileNet® P8. Một số trong các cách tiếp cận hiển nhiên
đã ẩn giấu các mối nguy hiểm, nhưng có một cách tiếp cận đúng và có ích là đơn
giản và hiệu năng. Cùng với cách giải quyết vấn đề chung này, chúng ta sẽ thấy
một số vấn đề về phát triển P8 có quy mô lớn hơn nhiều.
Mở đầu
Thói quen ở nhiều nơi có các cửa hàng nhỏ là nắm được các khách hàng đã xếp
hàng bằng cách gán cho họ các số thứ tự sát với thứ tự mà họ tới cửa hàng. Các số
thứ tự này thường được in trên các mảnh giấy nhỏ và được phân phối từ một bộ
phân phối cơ khí vật lý duy nhất. Nếu có nhiều khách hàng đến đồng thời, mối
quan hệ này dễ dàng bị phá vỡ bởi phép xã giao và phép lịch sự thông thường.
TỪ CHỐI BẢO HÀNH
Mã đi kèm là mã ví dụ do Tập đoàn IBM tạo ra. Mã ví dụ này không phải là một
phần của bất kỳ tiêu chuẩn hay sản phẩm nào của IBM và được cung cấp cho bạn
chỉ với mục đích giúp bạn trong việc phát triển các ứng dụng của mình. Mã này
được quy định "như nó vốn có", không bảo hành bất kỳ loại nào. IBM không chịu
trách nhiệm về mọi thiệt hại phát sinh do việc bạn sử dụng mã ví dụ này, ngay cả
khi bạn đã được biết về khả năng thiệt hại như vậy.
Các vấn đề tương tự thường phát sinh trong các hệ thống phần mềm. Một hệ thống
thường cần gán các số cho các việc nào đó với đảm bảo rằng các số đó là duy nhất
và theo một số mô hình. Có một số giải pháp chung cho vấn đề này, nhưng các hệ
thống phân phối làm phức tạp các vấn đề. Hầu như không chắc rằng bạn sẽ sử
dụng một hệ thống ECM để gán các số cho các khách hàng tại một hiệu bánh ở
góc phố. (Nhưng nếu bạn đã quan tâm về điều đó, tôi có thể nối bạn với một người
bán hàng có thiện ý!) Tuy nhiên, bạn có thể cần gán các số theo trường hợp hoặc
các số mã định danh (ID) khách hàng hoặc các số bộ phận hoặc một cái gì đó đơn
giản hơn. Các nhà cung cấp cơ sở dữ liệu triển khai thực hiện các kiểu cột số thứ
tự chỉ dùng cho loại vấn đề này. Tuy nhiên, P8 không cung cấp truy cập trực tiếp
vào các kiểu số thứ tự cơ sở dữ liệu, vì vậy bạn phải sử dụng các cơ chế khác.
Trong bài này, chúng ta sẽ việc xem xét cách giải quyết vấn đề này trong một môi
trường P8. Hãy tóm tắt các yêu cầu:
1. Chúng ta cần gán các số được bảo đảm tuyệt đối là duy nhất. Hoàn toàn
không chấp nhận gán hai lần với cùng một số.
2. Chúng ta muốn các số đi theo một vài mô hình. Chúng ta không muốn có
những cách quãng trong việc gán số. Mô hình có thể có nhiều thứ, nhưng
với các mục đích của mình chúng ta sẽ chỉ sử dụng mô hình tăng dần đơn
giản. Số tiếp theo mà chúng ta nhận được sẽ là một số lớn hơn số đứng
trước.
3. Chúng ta muốn tất cả điều này hoạt động tin cậy và có hiệu năng tốt trong
một môi trường P8 có nhiều luồng, nhiều bộ xử lý, nhiều máy chủ, nhiều
tầng và nhiều người dùng.
4. Trong khi chúng ta đang ở đó, chúng ta cần khoảng một chục bánh nướng
nhỏ màu đỏ trông rất ngon có lớp phủ kem pho mát!
Trước khi mô tả việc triển khai thực hiện ưa thích của mình, đầu tiên chúng ta sẽ
xem xét một vài kỹ thuật hoặc không làm việc hoặc không làm việc rất tốt. Ngay
cả khi bạn chưa bao giờ có nhu cầu triển khai thực hiện trường hợp sử dụng cụ thể
này, hầu hết các điểm được minh họa trong bài này có thể áp dụng cho nhiều lĩnh
vực lập trình P8.
Đồng bộ Java hay .NET
Nếu bạn là người mới đến với công việc phát triển doanh nghiệp hay phát triển
phân phối nói chung, bạn có thể nghĩ ngay đến việc sử dụng một đối tượng đơn lẻ
có một số kiểu truy cập đồng bộ hóa đến phần cập nhật bộ đếm. Trong Java, đó sẽ
là một phương thức synchronized (đã đồng bộ) hoặc khối mã. Trong C#, sẽ là một
phương thức đánh dấu là đã đồng bộ hoặc một khối mã được bảo vệ bằng một
lock(). Các vùng mã có truy cập đồng bộ đôi khi được gọi là các phần tới hạn. Liệt
kê 1 cho thấy một trong nhiều cách để thực hiện điều này.
Liệt kê 1. Khối mã đồng bộ
/**
* *** DON'T DO IT THIS WAY ***
*/
public class Dispenser
{
/** static access only, so private constructor */
private Dispenser() {}
private static int counter = 0;
public static final synchronized int getNextValue()
{
return ++counter;
}
}
Việc sử dụng mã đồng bộ là tốt cho các vấn đề cụ thể, nhưng với trường hợp sử
dụng của chúng ta các nhược điểm là khá rõ. Vì giá trị của bộ đếm chỉ tồn tại trong
bộ nhớ của chương trình đang chạy, nên các giá trị này sẽ bắt đầu lại khi chương
trình được khởi động lại. Bạn có thể thay đổi lớp Dispenser (Bộ phân phối) để lưu
các giá trị đã cập nhật vào một tệp, nhưng điều đó sẽ dẫn đến các vấn đề mới do
không thể phối hợp đồng bộ qua các ranh giới của quá trình. Các ứng dụng chạy
độc lập khác nhau (hoặc các bản sao của cùng một ứng dụng), ngay cả khi chúng
đang sử dụng lớp Dispenser, có thể đã xen kẽ đọc và viết vào tệp. Tệ hơn nữa,
chúng có thể đang truy cập vào một tệp có cùng tên trên các máy tính khác nhau.
Cả hai tình huống đó đều dẫn đến các điều kiện vi phạm các yêu cầu của trường
hợp sử dụng của chúng ta.
Dưới đây là một ví dụ về những rắc rối có thể do các việc đọc và viết xen kẽ. Giả
sử giá trị hiện tại được ghi trong tệp là 7 và sau đó các việc sau đây diễn ra:
A đọc giá trị 7 từ tệp này.
B đọc giá trị 7 từ tệp này.
A viết giá trị 8 vào tệp này.
C đọc giá trị 8 từ tệp này.
C viết giá trị 9 vào tệp này.
B viết giá trị 8 vào tệp này.
Lúc này, cả hai A và B có cùng một số mà họ tin là duy nhất với họ. Người đọc
tệp tiếp theo sẽ kết thúc bằng một giá trị giống như C đã có. Bạn có thể có một vài
ý tưởng về việc sử dụng khóa tệp hoặc các thủ thuật cụ thể của hệ điều hành khác
để đồng bộ hóa truy cập vào một tệp trong một môi trường phân phối. Tuy nhiên,
những khó khăn với kiểu giải pháp đó cũng khá dễ hiểu trong hầu hết ngành khoa
học máy tính và quả là rất khó khăn để nhận diện chính xác nó, do đó, chúng ta
không cần chỉ trích kịch bản này quá nhiều. Nếu bạn cần có sức thuyết phục hơn,
hãy sử dụng máy tìm kiếm ưa thích của mình để nghiên cứu "vấn đề khóa NFS".
Đối tượng bộ phân phối P8
Sử dụng một servlet J2EE
Việc mô tả nó đầy đủ nằm ngoài phạm vi của bài viết này, nhưng có thể sử dụng
an toàn khai báo của J2EE để cung cấp truy cập superuser có kiểm soát tới một
đối tượng P8. Kỹ thuật đó có thể làm việc với cả hai máy khách Java và .NET.
Tóm lại, bạn cần tạo ra một servlet đơn giản để triển khai thực hiện các kỹ thuật
được thảo luận trong phần này. Điều này có thể được thực hiện như một dịch vụ
web hoặc một cái gì đó tương tự miễn là nó trả về giá trị bộ đếm cho người gọi để
sử dụng theo một số định dạng dễ dùng.
Bộ mô tả triển khai cho một servlet J2EE cung cấp đặc tả của một vai trò RunAs
gián tiếp chỉ định một người ủy thác an toàn. Các cuộc gọi J2EE hướng xuống
(bao gồm các cuộc gọi P8 bằng cách sử dụng truyền tải EJB) được thực hiện bằng
nhận dạng an toàn của người ủy thác an toàn đã định. Trong trường hợp của chúng
ta, đó sẽ là một người dùng có các quyền truy cập thích hợp tới đối tượng của bộ
phân phối bị khóa hướng xuống. Ngay cả khi nhận dạng an toàn được cấu hình đã
là một người dùng có đặc quyền khá cao, thì kỹ thuật này là an toàn vì servlet
kiểm soát chặt chẽ những hoạt động nào được phép. Tuy nhiên, kỹ thuật này vì lẽ
đó trở nên kém an toàn hơn do sự phức tạp của servlet đang tăng lên.
Không chỉ kỹ thuật này phân tách các khía cạnh truy cập an toàn, nhưng hiệu quả
một hướng thú vị của việc sử dụng servlet là nó cách ly logic nghiệp vụ của việc
nhận một số thứ tự. Bất kỳ số lượng máy khách tạp nham nào với bất kỳ số lượng
các ngăn xếp công nghệ đều có thể truy cập servlet và nhận được một số thứ tự.
Bất lợi rõ ràng là sự phức tạp của việc thêm servlet vào kiến trúc đó và đưa ra việc
thăm dò máy khách để truy cập nó trong suốt. Cho dù bất lợi đó là đáng kể thì nó
vẫn phụ thuộc vào các đặc điểm kịch bản của bạn.
Một giải pháp phổ biến cho những vấn đề này là sử dụng một cơ sở dữ liệu để duy
trì bộ phân phối. Các cơ sở dữ liệu quan hệ có sẵn cho doanh nghiệp vốn đã phân
phối tài nguyên với ngữ nghĩa khóa tin cậy. Trong kiến trúc P8, các ứng dụng
không thể truy cập trực tiếp vào các cơ sở dữ liệu phía sau. Nói cách khác, việc
truy cập cơ sở dữ liệu phải được bố trí riêng cho các ứng dụng này, cho dù nó
thông qua các nguồn dữ liệu J2EE, nhằm vào các kết nối JDBC, hoặc các phương
tiện khác. Việc này có thể làm việc tốt trong một số kịch bản, nhưng nói chung
thật là phiền toái để làm việc này chỉ để truy cập dữ liệu bộ phân phối.
Một điều mà chúng ta biết là tất cả các ứng dụng dựa vào P8 có quyền truy cập
đến ObjectStores và các đối tượng P8, phải chịu các sự kiểm tra truy cập do P8 bắt
buộc. Do đó chúng ta có thể mô hình hóa bộ phân phối như là một đối tượng P8.
Cụ thể, chúng ta có thể tạo ra một cá thể của một lớp con của CustomObject gọi là
WjcDispenser với một đặc tính tùy chỉnh số nguyên được gọi là WjcCounter. (Gắn
cho các tên tiền tố tùy ý "Wjc" này để tránh các xung đột với tên lớp và tên thuộc
tính khác). Hình 1 cho thấy một biểu đồ UML cho lớp con đơn giản này.
Hình 1. Biểu đồ UML cho WjcDispenser
Chúng ta giả định rằng truy cập an toàn đến đối tượng này có thể được bố trí thuận
tiện cho tất cả người dùng của tất cả các ứng dụng cần sử dụng nó. Bây giờ, chúng
ta sẽ giả định rằng bất cứ ai cũng có thể kết nối tới ObjectStore và cập nhật đối
tượng bộ phân phối. Xem thanh bên Sử dụng một servlet J2EE để có một cách tiếp
cận thú vị tới tình huống an toàn này.
Hơn nữa, chúng ta sẽ che đậy việc tạo đối tượng bộ phân phối ban đầu. Một kỹ
thuật tốt sẽ là có các lớp Java và .NET kèm theo để phát hiện xem đối tượng bộ
phân phối có còn thiếu không và tạo nó theo yêu cầu hoặc trong bộ khởi tạo tĩnh
cho lớp đó. Hai kỹ thuật phổ biến để định vị đối tượng bộ phân phối là sử dụng
một giá trị ID được định sẵn hoặc để lưu trữ đối tượng này trong một đường dẫn
được định sẵn trong ObjectStore. Cũng có thể sử dụng một truy vấn để tìm ra tất
cả các cá thể của lớp WjcDispenser. Với các ví dụ tiếp theo, chúng ta giả định
rằng việc nhận dạng ObjectStore và vị trí cụ thể của đối tượng bộ phân phối dù thế
nào đi nữa cũng được cấu hình cho ứng dụng này.
Các khóa hợp tác của Máy nội dung (CE) FileNet
Khi sử dụng một đối tượng bộ phân phối dựa trên P8, ý tưởng dựa trên khái niệm
để nhận được một số thứ tự là giống nhau: đọc giá trị cũ, cập nhật và lưu giá trị
mới và trả về giá trị mới cho người gọi. Rõ ràng, các quá trình thực hiện cụ thể
thay đổi. Tất cả các nhược điểm về đồng bộ hóa Java hoặc .NET vẫn tiếp tục áp
dụng.
Máy chủ CE và các API triển khai thực hiện một tính năng gọi là khóa hợp tác.
Tính năng này ban đầu được triển khai thực hiện để cung cấp ngữ nghĩa khóa hợp
tác tương thích với RFC-2518 (WebDAV). Các lớp API cho Folder (Thư mục),
Document (Tài liệu) và CustomObject (Đối tượng tùy chỉnh) có các phương thức
để khóa và mở khóa các đối tượng đó. Vì đây là một tính năng P8 tích hợp sẵn
được triển khai thực hiện tại máy chủ, nên bạn có thể phát triển một việc thực hiện
tương tự như những gì được thể hiện trong Liệt kê 2. Ví dụ này cho thấy một
phương thức nội bộ và giả định một số đoạn mã khác đã nhận biết đối tượng bộ
phân phối.
Liệt kê 2. Khóa hợp tác P8
private static final String COUNTER_PROPERTY_NAME = "WjcCounter";
/**
* *** DON'T DO IT THIS WAY ***
*/
private static int getNextValue(CustomObject dispenser)
{
final Properties dispenserProperties = dispenser.getProperties();
// Object might be locked by someone else, so try a few times
for (int attemptNumber=0; attemptNumber<10; ++attemptNumber)
{
dispenser.lock(15, null); // LOCK the object for 15 seconds
try
{
// Because we use a refreshing save, the counter property
// value will be returned.
dispenser.save(RefreshMode.REFRESH); // R/T
break;
}
catch (EngineRuntimeException ere)
{
ExceptionCode ec = ere.getExceptionCode();
if (ec != ExceptionCode.E_OBJECT_LOCKED)
{
// If we get an exception for any reason other than
// the object already being locked, rethrow it.
throw ere;
}
// already locked; try again after a little sleep
try
{
Thread.sleep(100); // milliseconds
}
catch (InterruptedException e) { /* don't worry about this rarity */ }
continue;
}
}
int oldValue =
dispenserProperties.getInteger32Value(COUNTER_PROPERTY_NAME);
int newValue = oldValue + 1;
dispenserProperties.putValue(COUNTER_PROPERTY_NAME, newValue);
dispenser.unlock(); // UNLOCK the object
dispenser.save(RefreshMode.NO_REFRESH); // R/T
return newValue;
}
Giả sử rằng đối tượng bộ phân phối được khởi tạo theo cách không tìm nạp (có
nghĩa là, thông qua một phương thức Factory.CustomObject.getInstance()), kỹ
thuật này trị giá một chuyến đi khứ hồi đến máy chủ CE để áp dụng khóa và tìm
nạp giá trị thuộc tính hiện tại. Nếu đối tượng đã bị khóa, chúng ta sẽ không nhận
được giá trị hiện tại, vì vậy chúng ta lặp lại một vài lần để nhận được thay đổi của
mình lúc khóa bộ phân phối. Nó trị giá một chuyến đi khứ hồi khác đến máy chủ
CE để lưu trữ giá trị bộ đếm mới. Đó là một chi phí hiệu năng hợp lý cho điều này
và cũng hợp lý cho việc sử dụng toàn bộ tính năng khóa/mở khóa.
Vấn đề chính của việc sử dụng khóa hợp tác P8 cho trường hợp sử dụng này là nó
chỉ là khóa hợp tác. Máy chủ CE sẽ không ngăn cấm bất kỳ thay đổi nào do chỉ là
sự hiện diện của một khóa. Lạc quan là, bạn có thể giả định rằng tất cả các ứng
dụng sẽ đi theo trình tự khóa hợp tác. Thực tế, bạn vẫn phải tính đến khả năng các
lỗi ứng dụng bỏ qua việc khóa. Thật dễ dàng để tưởng tượng ai đó viết một đoạn
mã độc lập sử dụng đối tượng bộ phân phối nhưng bỏ qua thực hiện việc khóa.
Gắn với trình xử lý sự kiện
Đó là một lưu ý một phía, nhưng nếu bạn xem cuộc thảo luận về khóa hợp tác
trong phần trước, bạn thấy rằng phải mất ít nhất hai chuyến đi khứ hồi đến máy
chủ để chắc chắn có được một số thứ tự. Có thể thực hiện nó chỉ với một chuyến
đi khứ hồi không? Để thực hiện ý tưởng đó, chúng ta sẽ cần ít nhất một phần tính
toán được thực hiện trên máy chủ CE. Cơ chế CE cho điều đó là các trình xử lý sự
kiện. Dưới đây là một thử nghiệm tư duy:
Khởi tạo theo cách không tìm nạp đối tượng bộ phân phối (không có
chuyến đi khứ hồi).
Chúng ta cần phải thực hiện một số kiểu thay đổi cho đối tượng bộ phân
phối sao cho một sự kiện sẽ được bắt đầu. CE có một tính năng cho phép
bạn định nghĩa các sự kiện tùy chỉnh. Trên thực tế, lớp này được gọi là
CustomEvent. Như là một phần của việc thiết lập một lần cho tất cả những
điều này, hãy định nghĩa một sự kiện tùy chỉnh mới và duy trì nó trong
ObjectStore đang giữ đối tượng bộ phân phối.
Thay vì đưa ra như là một hiệu ứng phụ của một cái gì đó khác, một sự kiện
tùy chỉnh được kích hoạt khi bất kỳ ai gọi phương thức raiseEvent() trên
một đối tượng đăng ký được. Trên máy chủ, việc xử lý đăng ký và sự kiện
cũng giống như với các sự kiện do hệ thống định nghĩa. Hãy gọi
raiseEvent() trên đối tượng bộ phân phối và sau đó gọi save() có làm mới
(để có được giá trị hiện tại của WjcCounter).
save() sẽ kích hoạt CustomEvent.
Như là một phần của việc thiết lập một lần, hãy cung cấp một trình xử lý sự
kiện được đăng ký với kiểu CustomEvent cụ thể đó trên cá thể hoặc lớp
WjcDispenser. Trình xử lý sự kiện sẽ tính toán và lưu các giá trị mới cho
đặc tính WjcCounter. Do không thể cập nhật các đặc tính trong một trình
xử lý sự kiện đồng bộ, nên chúng ta sẽ thực hiện trình xử lý sự kiện không
đồng bộ (đồng bộ hoặc không đồng bộ được quy định lúc đăng ký).
Ứng dụng khách biết cách trình xử lý sự kiện sẽ cập nhật WjcCounter, vì
thế, nó thực hiện một tính toán tương tự để dự báo giá trị mới của
WjcCounter.
Trong thử nghiệm tư duy này, bạn tính rằng các cập nhật cho đối tượng bộ phân
phối sẽ xảy ra một lần tại một thời điểm, cho dù có bao nhiêu ứng dụng khách độc
lập đang yêu cầu nó đi nữa. Máy chủ CE thì không "tối ưu hóa ngay" các cập nhật
trung gian (dự phòng). Bạn hãy nhớ rằng cần bảo đảm các trình xử lý sự kiện
không đồng bộ được thực hiện. Trên thực tế, bạn thậm chí có thể nhớ là đã nghe ở
đâu đó rằng các trình xử lý sự kiện không đồng bộ được xử lý qua hàng đợi (đó là
đúng). Tất cả điều đó dường như thêm vào một cập nhật duy nhất, tin cậy và dự
báo được tới WjcCounter cho mỗi chuyến đi khứ hồi của máy khách đến máy chủ
CE.
Bạn có thể đã đoán được từ giọng điệu mô tả ở trên là có một số vấn đề bí mật ở
đây. Trên thực tế, có hai vấn đề. Thứ nhất, có một khoảng thời gian ngắn giữa việc
cập nhật đối tượng bộ phân phối trong ObjectStore và việc thực hiện trình xử lý sự
kiện không đồng bộ. Nếu có nhiều máy khách độc lập tình cờ thực hiện cấp nhật
cùng một lúc, thì tất cả chúng sẽ thấy cùng một giá trị được làm mới của
WjcCounter và tính toán giá trị cập nhật giống nhau. Thậm chí nếu bạn có thể
khắc phục điều đó và bằng cách nào đó buộc các hoạt động save() của máy khách
cụ thể vào khởi động cụ thể trình xử lý sự kiện, thì sẽ có một vấn đề thứ hai. Vấn
đề thứ hai là mặc dù các sự kiện không đồng bộ được xử lý thực sự thông qua một
hàng đợi, cần có nhiều thiết bị đọc hàng đợi. Vì vậy, không có gì bảo đảm rằng
các trình xử lý sự kiện không đồng bộ sẽ thực hiện theo thứ tự giống như các cập
nhật thực hiện nhanh.
Người viết đầu tiên sẽ thắng
Máy chủ CE có một tính năng tích hợp sẵn để phát hiện tin cậy các cập nhật xen
kẽ. CE triển khai thực hiện một chính sách gọi là người viết đầu tiên sẽ thắng.
Điều đó có nghĩa rằng nếu có hai yêu cầu đang cập nhật cùng một đối tượng, yêu
cầu đầu tiên sẽ thành công còn yêu cầu thứ hai sẽ thất bại. Với việc cập nhật thất
bại này, máy chủ sẽ đưa ra một EngineRuntimeException với một ExceptionCode
của E_OBJECT_MODIFIED. Thông báo ngoại lệ đi kèm là "Đối tượng ... đã
được sửa đổi vì nó đã được lấy ra". Thế điều đó có nghĩa gì?
Mỗi đối tượng có thể tồn tại độc lập trong một ObjectStore được đánh dấu bằng
một Số thứ tự cập nhật (USN - Update Sequence Number). Đây không phải là một
đặc tính theo nghĩa thông thường, nhưng giá trị của nó được trưng ra qua phương
thức IndependentlyPersistableObject.getUpdateSequenceNumber(). Máy chủ CE
tự động gia tăng USN bất cứ khi nào một đối tượng được cập nhật trong
ObjectStore. Khi bạn tìm nạp một đối tượng từ máy chủ, USN cũng được tìm nạp
và được đưa đến phía máy khách. Các API gửi USN đó trở về máy chủ như là một
phần của đối tượng save(). Nếu giá trị USN đã gửi đi không khớp với giá trị USN
hiện tại đã có trong kho lưu trữ, thì máy chủ CE biết rằng đã có một sự thay đổi
xảy ra (bởi một số người gọi khác) kể từ khi đối tượng này đã được tìm nạp. Điều
này biểu thị một dạng đơn giản của cái mà thế giới cơ sở dữ liệu gọi là khóa lạc
quan.
Cụ thể là máy chủ CE phát hiện những thay đổi xen kẽ này, đó là logic để khai
thác điều đó hơn là tính năng khóa hợp tác tự nguyện hoàn toàn. Thông qua các
API của CE, bạn có thể sử dụng một cái gì đó gọi là khởi tạo không tìm nạp
(fetchless instantiation), mà chúng ta đã đề cập ở đâu đó, để bỏ qua việc kiểm tra
máy chủ, nhưng bạn phải thoát khỏi cách làm việc đó của mình với trường hợp sử
dụng này. Khi bạn khởi tạo tại chỗ một đối tượng trong API mà không cần tìm nạp
nó từ máy chủ, thì đó chính là khởi tạo không tìm nạp. Trong trường hợp này, giá
trị USN có một giá trị đặc biệt để báo hiệu cho máy chủ CE biết bỏ qua việc kiểm
tra USN. Điều này đôi khi được gọi là một cập nhật không được bảo vệ. Nếu sau
đó bạn