'Framework/Another Lore'에 해당되는 글 27건

  1. 2009.04.24 AL : Link
  2. 2009.04.24 AL : Observation
  3. 2009.04.24 AL : Versioning Model
  4. 2009.04.23 AL : Workspace
  5. 2009.04.23 AL : Node
  6. 2009.04.23 AL : Property
  7. 2009.04.23 AL : PropertyDefinition
  8. 2009.04.23 AL : NodeType
  9. 2009.04.23 AL : Lock
  10. 2009.04.23 AL : sequence
Framework/Another Lore2009. 4. 24. 09:45


Link는 다른 ORMapping이나 JSR283 API에는 나오지 않는 좀 독특한 API이다.

Link는 Node와 Node의 관계이다. Node는
   Node session.createNode(Node parent, NodeType nodetype, String name)
parentNode를 지정하고 create되는데(맨 처음 만들게 되는 root 노드는 NO_PARENTNODE 라는 Node를 ParentNode로 설정한다.) 이때 parentNode와 createdNode 사이에는 자동적으로 Parent-Child Link가 생성된다.

이 자동적으로 생성된 Link를 통해
  Iterator<Node> Node.getChildNode(Page page) API가 동작한다.

그리고 Link는 또한 User가 수동적으로 create시킬수 있다. 가장 대표적인 경우가 MemberType인데

Node boardNode = createNode(root, boardType, "general_board")
Node arcileNode = createNode(boardNode, articleType, sequence.nextVal("board");
articleNode.setSubject("제목") ;
articleNode.setContent("내용") ;

와 같은 코드를 통해 게시판을 구현한다고 했을때.

boardNode와 articleNode는 parent-child 관계이다. 그러나 parent-child 관계는 orderable 하지 않기 때문에 순서가 보장되어야 하는 게시판에는 적당하지 않다.

그럴경우
boardNode.addLink(articleNode, new LinkType("belongTo"), Position.After) 와 같이 명시적으로 새로운 관계를 지정할 수 있다. boardNode는 articleNode와 belongTo 관계를 가지며 관계의 맨끝에 넣으라는 API이다. 그러나 일반적으로 관계는 targNode의 nodeType을 제한하지 않는다. 그래서  Iterator<Node> parentNode.getNodes(new LinkType("belongTo")) 라고 했을때 모두 articleTtype이 나올것이라고 확신할수 없다.

그러나 이런경우라면 굳이 새로운 linkType을 만들 필요는 없다. 이미 이러한 역할을 하는 관계가 있는데 memberType이다.
boardNodeType.setMemberType(articleNodeType) 이라고 지정하면 boardNodeType의 instance인 boardNode는 articleNodeType 혹은 articleNodeType을 상속받은 Node만 member로 지정할 수 있다.

memberLinkType은 order를 지정할 수 있고
   boardNode.getMemberNode(Page page) 와 같이 억세스 할 수 있다.

이미 정의된 parent-child 관계와 member 관계 말고도 원할 경우 어떠한 Link 관계도 맺을 수 있다. 이를테면 boardNode는 특정 articleNode와 mostPopular 관계를 맺어 이를 반복해서 노출시킬 수 있다. 또는 특정 Node끼리 same link를 맺을 수도 있다.

Link의 존재로 인해 Node들을 더 유연하게 참조할 수 있게 된다.

Direction
Link는 FromNode->ToNode의 단방향이다.


[Example Code]

  Node r1 = createNode(root, robotType, "1") ;
  Node r2 = createNode(root, robotType, "2") ;
  Node r3 = createNode(root, robotType, "3") ;
  Node r4 = createNode(root, robotType, "4") ;
  Node r5 = createNode(root, robotType, "5") ;
  
  robotType.setMemberType(robotType) ;
  r1.addLink(r2, LinkTypeImpl.MEMBER, Position.AFTER) ;
  r1.addLink(r3, LinkTypeImpl.MEMBER, Position.AFTER) ;
  r1.addLink(r4, LinkTypeImpl.MEMBER, Position.AFTER) ;
  r1.addLink(r5, LinkTypeImpl.AGREE, Position.AFTER) ;
  
  session.save() ;
  
  assertEquals(3, r1.getLinks(LinkTypeImpl.MEMBER, Page.ALL).size()) ;

  assertEquals(2, r1.getLinks(LinkTypeImpl.MEMBER, Page.create(2, 1)).size()) ;
  assertEquals(1, r1.getLinks(LinkTypeImpl.MEMBER, Page.create(2, 2)).size()) ;
  assertEquals(0, r1.getLinks(LinkTypeImpl.MEMBER, Page.create(2, 3)).size()) ;

  assertEquals(2, r1.getLinks(Page.create(2, 1)).size()) ;
  assertEquals(2, r1.getLinks(Page.create(2, 2)).size()) ;
  assertEquals(0, r1.getLinks(Page.create(2, 3)).size()) ;


<about Link interface in Node >
 public GenericIterator<Node> getChildNode(int maxDepth) throws RepositoryException ;
 public GenericIterator<Node> getChildNode(Page page) throws RepositoryException;
 public GenericIterator<Node> getChildNode() throws RepositoryException;
 public GenericIterator<Node> getMemberNode(Page page) throws RepositoryException ;
 public GenericIterator<Node> getNodes(LinkType type, Page page) throws RepositoryException;

 public GenericIterator<Link> getLinks(Page page) throws RepositoryException;
 public GenericIterator<Link> getLinks(LinkType linkType, Page page) throws RepositoryException  ;



<interface Link extends IConstant, Publishable>

 public void setMode(Mode mode) ;
 public Mode getMode() ;
 public Node getFrom() throws RepositoryException ;
 public LinkType getLinkType();
 public Node getTo() throws RepositoryException ;
 public String getFromUUID() ;
 public String getToUUID() ;
 public Position getPosition() ;







'Framework > Another Lore' 카테고리의 다른 글

AL : Property Type Conversion  (0) 2009.04.26
AL : Reading  (0) 2009.04.25
AL : Observation  (0) 2009.04.24
AL : Versioning Model  (0) 2009.04.24
AL : Workspace  (0) 2009.04.23
Posted by bleujin
Framework/Another Lore2009. 4. 24. 08:42



A repository may support observation, which enables an application to receive notification of persistent changes to a workspace.

Event Model
A persisted change to a workspace is represented by a set of one or more events. Each event reports a single simple change to the structure of the persistent workspace in terms of an item added, changed or removed. The six
standard event types are:

NODE_CREATED, NODE_REMOVED
PROPERTY_CREATED, PROPERTY_CHANGED
LINK_CREATED, LINK_REMOVED


Scope of Event Reporting


The scope of event reporting is implementation-dependent. An implementation should make a best-effort attempt to report all events, but may exclude events if reporting them would be impractical given implementation or resource limitations. For example, on an import, create or remove of a subgraph containing a large number of items, an implementation may choose to report only events associated with the root node of the affected graph and not those for every subitem in the structure.

Event implement
NodeEvent, PropertyEvent, LinkEvent


Event Information
Each Event is associated with a path, an identifier and an information map, the interpretation of which depend upon the event type.

The event path is retrieved through
  NodeEvent.getNode()
  PropertyEvent.getProperty() ;
  LinkEvent.getLink() ;

User ID
An Event also records the identity of the Session that caused it. String Event.getUserID()
String Event.getUserID()
returns the user ID of the Session, which is the same value that is returned by Session.getUserID()


Event Date

An event also records the time of the change that caused it. This acquired through
long Event.getDate()
The date is represented as a millisecond value that is an offset from the epoch January 1, 1970 00:00:00.000 GMT (Gregorian). The granularity of the returned value is implementation-dependent.


Event Ordering

In both asynchronous and journaled observation the order of events within a bundle and the order of event bundles is not guaranteed to correspond to the order of the operations that produced them.

Observation Manager

Registration of event listeners is done through the ObservationManager object acquired from the Workspace through
ObservationManager Workspace.getObservationManager().


Exceptions

The method EventListener.onEvent does not specify a throws clause. This does not prevent a listener from throwing a RuntimeException, although any listener that does should be considered to be in error.


[Example Code]
  EventStackingListener listener = new EventStackingListener();
  workspace.getObservationManager().addEventListener(listener) ;
  
  Node node = session.createNode(root, newType, "abc") ;
  
  assertEquals(3, listener.getCount())  ;
  
  NodeEvent event = (NodeEvent)listener.popEvent();
  assertEquals(EventType.NODE_CREATED, event.getType()) ;
  assertEquals(node, event.getNode()) ;
  assertEquals(EventType.LINK_CREATED, listener.popEvent().getType()) ;
  assertEquals(EventType.PROPERTY_CREATED, listener.popEvent().getType()) ; // because of default value
  
  node.setProperty(IDString.propId("firstname"), "bleujin") ;
  assertEquals(EventType.PROPERTY_CHANGED, listener.popEvent().getEventType()) ;


<interface Event>
 public EventType getType() 
 public String getUserID() 
 public long getDate()


<interface EventListener>
 public void onEvent(Event event);
 public boolean isDealWith(Event event);

'Framework > Another Lore' 카테고리의 다른 글

AL : Reading  (0) 2009.04.25
AL : Link  (0) 2009.04.24
AL : Versioning Model  (0) 2009.04.24
AL : Workspace  (0) 2009.04.23
AL : Node  (0) 2009.04.23
Posted by bleujin
Framework/Another Lore2009. 4. 24. 05:28


Versioning enables a user to record the state of a node and its subgraph and restore that state at a later time.


Check-In

When node is checked-in, a new version of that node is created which contains a (typically partial) copy of its subgraph. The part of a node’s subgraph that is to be copied to a version is referred to as its versionable
subgraph and constitutes its versionable state. 
A new version of a versionable node is created using
    Version node.checkIn()
This method require exsting node in repository. Created Node but not Saved Node(only exists in memory) may throw a RepositoryException.


Version History

Once created, a version is stored in a version history
The method
    VersionHistory node.getVersionHistory()


Check-Out

Once checked-in, a node and its versionable subgraph become readonly. To alter a checked-in node or its versionable subgraph, the node must first be checked-out. It can then be changed and checked-in again, creating a new version.
The method
    version.checkOut()


Base Version Reference

Each versionable node has a base version within its version history. When a new version of a node is created, it is placed in that node’s version history as a direct successor of the base version. That version itself then becomes the new base version
The method
    Node versionHistory.getRecentNode()


Version Extends Node
The Version interface extends Node.



<interface Version extends Node, Comparable<Version>>
 public Calendar getCreated();
 public void checkOut() throws RepositoryException;
 public Node getRecentNode()throws RepositoryException;
 public GenericIterator<Property> getProperties() throws RepositoryException;
 public Property getProperty(IDString propId) throws RepositoryException;


<interface VersionHistory extends Publishable, IConstant>
 public GenericIterator<Version> getAllVersions();
 public Version getVersion(int versionIndex);
 public Node getRecentNode();

'Framework > Another Lore' 카테고리의 다른 글

AL : Link  (0) 2009.04.24
AL : Observation  (0) 2009.04.24
AL : Workspace  (0) 2009.04.23
AL : Node  (0) 2009.04.23
AL : Property  (0) 2009.04.23
Posted by bleujin
Framework/Another Lore2009. 4. 23. 12:26


Workspace

AnotherLore repository is composed of one or more persistent workspaces, each of which is a persistent data store consisting of a directed acyclic graph of items where the edges represent the parent-child relation. Each persistent workspace is identified by a name, unique within the repository, which is a string.

this have ValidChain, ObservationManager, LockManager, IndexDirectory, GlobalStore, LocalStore


Storage

GlobalStore : The node type of a AnotherLore is stored, and Sequence, NamespaceRegistry

LocalStore : The nodes of a workspace form the structure of the stored data while the actual content is stored in the values of the properties.


Sessions

All interaction with the repository occurs through a session. A user receives a session upon connecting to the repository by supplying a set of credentials and the name of a persistent workspace to which the user wishes to connect. The returned session binds the user to the requested persistent workspace with a level of authorization determined by that user's credentials. A session is always bound to exactly one persistent workspace, though a single persistent workspace may be bound to multiple sessions.


User

A user is any agent bound to a session. This may be a human user, an external software process, or anything else that holds and controls the session.

Through a session, the user can access, read and write the nodes and properties of the bound workspace, to the extent allowed by that user's authorization and the capabilities of the repository. Any object acquired, directly, or indirectly through a chain of interceding objects, from a particular session, is said to be within the scope of that session and any method called on such object is also within the scope of the same session.

In the context of discussing a particular object or method call, the session within whose scope that object or method call lies is referred to as the current session, and the workspace to which that session is bound is referred to as the current workspace.


Namespaces

nodetype name is an ordered pair of strings: (N, L)
where N is a AnotherLore namespace and L is a local name in a workspace

namespace is a Universal Resource Identifier. and can access other AnotherLore through namespace URI




'Framework > Another Lore' 카테고리의 다른 글

AL : Observation  (0) 2009.04.24
AL : Versioning Model  (0) 2009.04.24
AL : Node  (0) 2009.04.23
AL : Property  (0) 2009.04.23
AL : PropertyDefinition  (0) 2009.04.23
Posted by bleujin
Framework/Another Lore2009. 4. 23. 12:12

A node can have zero or more child property while a property cannot have children but can hold zero or more values.

Each workspace contains at least one item, the root node, which, uniquely among the items in a workspace, has no parent node. All other items have at least one parent.

The nodes of a workspace form the structure of the stored data while the actual content is stored in the values of the properties.(LocalStore)


Name
The name of the root node of a workspace is always “/”. Every other item in a workspace has the same name relative to each of its parent nodes. This name's type must be a alphaNumUnderBar except root node

For a given node, no two of its properties can have the same name. However, a node and property may have the same name and, in some cases, two or more nodes may have the same name.

Same-Name Siblings is not supported.


Path

The location of an Node in the workspace graph can be described by the path from the root node to that item, consisting of the name of each interceding node in order from root to target item, much like a file system path. Relative paths can also be used to describe the location of one item with respect to another. in A AnotherLore, path is unique



Identifiers

Every node also has an identifier, which, in some implementations, may be independent of its path and provide a second, more stable, form of address for a node. in all distributed AnotherLore, UUID is unique


Property

Properties can be single valued. The value of a property has a type, which is one of 11 possible types . The supported
property types include standard data types such as strings, numbers and dates, as well as pointers to other items in the same workspace (either via path or identifier), which can be used to define relationships between node structures
that cross-cut the workspace hierarchy.



Node Types


Nodes also have types. The type of a node defines which properties and child nodes that node may (or must) have, and the types of those child items in turn. Node types can be used to define complex storage objects consisting of multiple subnodes and properties, possibly many layers deep.


'Framework > Another Lore' 카테고리의 다른 글

AL : Versioning Model  (0) 2009.04.24
AL : Workspace  (0) 2009.04.23
AL : Property  (0) 2009.04.23
AL : PropertyDefinition  (0) 2009.04.23
AL : NodeType  (0) 2009.04.23
Posted by bleujin
Framework/Another Lore2009. 4. 23. 11:17

A Property object represents the smallest granularity of content storage.  A property must have one and only one parent node. A property does not have children. When we say that node A "has" property B it means that B is a child of A. A property consists of a name and a value.

All data stored within a AL repository is ultimately stored as the values of properties.


Property Types

STRING properties store instances of java.lang.String.

BOOLEAN properties store instances of the Java primitive type boolean.

LONG properties store instances of the Java primitive type long.

DOUBLE properties store instances of the Java primitive type double.

BINARY properties store instances of File

DATE properties store instances of java.util.Calendar.

NAME properties store instances of AlphaNumUnderBar

PATH properties store instances of AL paths and serve as pointers to locations within the workspace.PATH properties do not enforce referential integrity.

REFERENCE properties serve as pointers to referenceable nodes by storing their identifiers. REFERENCE properties do not enforce referential integrity

TEXT properties store instances of long length java.lang.String.

VIRTUAL_TEXT properties store template of string type


Property Type Conversion

When the value of a property is read or written using a type different from that declared for the property, the repository attempts a type conversion according to the following rules. Note that even in cases where the JCR type conversion is
defined in terms of standard JDK type conversion method, failure of conversion must only ever cause a JCR ValueFormatException to be thrown and never any exception defined in the JDK API.


Value Length

The length of a value is defined as follows:
For a BINARY value, its length is equal to its length in bytes. This number is returned both by Binary.getSize and by
Property.getLength and Property.getLengths

For other types, the length is the same value that would be returned by calling java.lang.String.getByte("UTF-8"). This number is returned by Property.getLength


[Example Code]
  IDString _string = IDString.propId("string");
  IDString _binary = IDString.propId("binary");
  IDString _long = IDString.propId("long");
  IDString _double = IDString.propId("double");
  IDString _date = IDString.propId("date");
  IDString _boolean = IDString.propId("boolean");
  IDString _name = IDString.propId("name");
  IDString _path = IDString.propId("path");
  IDString _reference = IDString.propId("reference");
  IDString _text = IDString.propId("text");
  IDString _integer = IDString.propId("integer");
  IDString _ftext = IDString.propId("ftext");
  PropertyDefinition[] pds = new PropertyDefinition[]{
    createProperty(_string, Property.Type.STRING), 
    createProperty(_binary, Property.Type.BINARY), 
    createProperty(_long, Property.Type.LONG), 
    createProperty(_double, Property.Type.DOUBLE), 
    createProperty(_date, Property.Type.DATE), 
    createProperty(_boolean, Property.Type.BOOLEAN), 
    createProperty(_name, Property.Type.NAME), 
    createProperty(_path, Property.Type.PATH), 
    createProperty(_reference, Property.Type.REFERENCE), 
    createProperty(_text, Property.Type.TEXT), 
    createProperty(_integer, Property.Type.INTEGER), 
    createProperty(_ftext, Property.Type.VIRTUAL_TEXT)
  } ;
  
  NodeType allType = createNodeType(objectType, "allType", pds) ;

  Node node = createNode(root, allType, "allType") ;
  node.setProperty(_string, (Object)"123") ;
  node.setProperty(_binary, (Object)TestValue.binaryValue) ;
  node.setProperty(_long, (Object)12311212121212L) ;
  node.setProperty(_double, (Object)123.3) ;
  Calendar cal = Calendar.getInstance();
  cal.setTime(DateUtil.string2Date("20060504-141224")) ;
  node.setProperty(_date, (Object)cal) ;
  node.setProperty(_boolean, (Object)true) ;
  node.setProperty(_name, (Object)"namedd") ;
  node.setProperty(_path, (Object)"path...") ;
  node.setProperty(_reference, (Object)root.getIdentifier()) ;
  node.setProperty(_text, (Object)new StringBuffer("abcdd")) ;
  node.setProperty(_integer, (Object)123) ;
  node.setProperty(_ftext, (Object)"$node.getUUIDString()") ;
  session.save() ;
  
  
  GenericIterator<Property> properties = node.getProperties() ;
  while(properties.hasNext()){
   Property property = properties.next() ;
   assertEquals(allType.getMappedDefinition(property.getId()).requiredType(), property.getValue().getPropertyType()) ;
  }
  
  assertEquals("123", node.getProperty(_string).getString()) ;
  assertEquals("111.jpg", node.getProperty(_binary).getString()) ;
  assertEquals("12311212121212", node.getProperty(_long).getString()) ;
  assertEquals("123.3", node.getProperty(_double).getString()) ;
  assertEquals("20060504-141224", node.getProperty(_date).getString()) ;
  assertEquals("true", node.getProperty(_boolean).getString()) ;
  assertEquals("namedd", node.getProperty(_name).getString()) ;
  assertEquals("path...", node.getProperty(_path).getString()) ;
  assertEquals(root.getIdentifier(), node.getProperty(_reference).getString()) ;
  assertEquals("abcdd", node.getProperty(_text).getString()) ;
  assertEquals("123", node.getProperty(_integer).getString()) ;
  assertEquals("$node.getUUIDString()", node.getProperty(_ftext).getString()) ;
  

  assertEquals("123", node.getProperty(_string).getObject()) ;
  assertEquals("jpg", ((BinaryPropertyValue)node.getProperty(_binary).getObject()).getContentType() ) ;
  assertEquals(12311212121212L, node.getProperty(_long).getObject()) ;
  assertEquals(123.3D, node.getProperty(_double).getObject()) ;
  assertEquals("20060504-141224", DateUtil.date2String((Calendar)node.getProperty(_date).getObject()) ) ;
  assertEquals(Boolean.TRUE, node.getProperty(_boolean).getObject()) ;
  assertEquals("namedd", node.getProperty(_name).getObject()) ;
  assertEquals("path...", node.getProperty(_path).getObject()) ;
  assertEquals(root.getIdentifier(), node.getProperty(_reference).getObject()) ;
  assertEquals("abcdd", node.getProperty(_text).getObject()) ;
  assertEquals(123, node.getProperty(_integer).getObject()) ;
  assertEquals(node.getIdentifier(), node.getProperty(_ftext).getObject()) ;
  
  node.setProperty(_boolean, "TRUE") ;
  assertEquals(true, node.getProperty(_boolean).getValue().getBoolean()) ;

  node.setProperty(_boolean, "true") ;
  assertEquals(true, node.getProperty(_boolean).getValue().getBoolean()) ;

  node.setProperty(_boolean, "1213") ;
  assertEquals(false, node.getProperty(_boolean).getValue().getBoolean()) ;

  try {
   node.setProperty(_long, "123a") ;
   fail() ;
  } catch(NotMatchePropertyTypeException ignore){
  }


< interface Property extends Cloneable, Serializable, IConstant, Publishable >
 public IDString getId();
 public Property newClone() throws RepositoryException;
 public Value getValue();
 public Property.Type getType() ;
 public String getTypeName();
 public String getString();
 public Node getNode() ;
 public void setValue(Value value);
 public boolean equalValue(Property property);
 public Object getObject() throws RepositoryException ;
 public Object getObject(Map param) throws RepositoryException;
 public void setMode(Mode mode) ;
 public Mode getMode() ;





3단계를 거치는 검사 정보를 저장하는 테이블을 설계한다고 하면 많은 프로그래머는

Create Table (processId varchar2(20), phase1 char(1), phase2 char(1), phase3 char(1)) 와 같이 만든다.


Process가 3단계라는건 어디까지나 현재시점이므로

Create Table(processId varchar2(20), phaseNo byte, phase char(1)) 로 하는게 좀더 유연하다.

processId당 1row를 읽었던 것을 3row로 읽기 때문에 성능이 떨어질 것이라고 생각하지만 IO는 byte의 양이며 양은 선이 아니라 넓이이다. 1 * 6 이나 3*2나 6*1이나 모두 양은 같으며 IO가 bit레벨이 아닌 kb의 일정한 단위로 이루어진다는 걸 생각하면 거의 영향이 없다. 오히려 동시성을 고려한 Lock 메커니즘을 생각하면 후자가 빠를때가 많다는 것은 DB에서는 상식에 가깝다. Table의 컬럼은 일종의 틀이며 제약이기 때문에 변할 가능성의 여부는 횡이 아니라 종에서 가능하도록 하면 설계의 유연성이 늘어난다. 대신에 1,2,3단계를 모두 통과한 process를 구하는 Query가 아주 조금 더 복잡해질 뿐이다.

이를 좀 더 극단적으로 생각하면 어떨까?
CREATE TABLE EMP
(
 EMPNO            NUMBER NOT NULL,
 ENAME            VARCHAR2 (10),
 JOB              VARCHAR2 (9),
 MGR              NUMBER (4),
 HIREDATE         DATE,
 SAL              NUMBER (7,2),
 COMM             NUMBER (7,2),
 DEPTNO           NUMBER NOT NULL
)
이라는 테이블은 극단적으로

Create Table property_tblc(
  propertyId varchar2(20) NOT NULL,
  propertyType varchar2(10) NOT NULL,
  propertyValue varchar(200)
)
로 바꿀수도 있다.

1102, bleujin, Dev, MGR, 2000.1.1, 100, 0, 20   이라는 row를

empNo, Number, 1102
ename, String, bleujin
job, String, Dev
mgr, String, MGR
....
와 같이 저장할수도 있다. 후자는 emp뿐 아니라 dept 테이블이라고 해도 모델이 변경될 필요가 없을정도로 극단적이다. 사실 이런 모델링은 이펙티브 오라클의 톰의 말대로 바보같은 모델링이다. 유연할지 모르겠지만 그 반작용은 아주 크다. 라고 죽 생각해 왔다.

그러나 경험상 30초짜리 쿼리를 3초로 줄이다든가 3초짜리 쿼리를 0.3초로 줄이면 클라이언트들은 아주 만족해 한다. 하지만 30ms 쿼리를 3ms 로 줄인다고 해서 기뻐하는 경우는 거의 없다.

다소 아이러니 하지만 ROI에서 본다면 당연한 이야기이다. 하나의 request에는 연산과 NetworkIO 등의 최소 한계 속도가 있다. CPU나 메모리의 연산속도는 ns지만 일반적으로 다루는 모든 I/O는 ms 단위로 이루어지게 된다. 30ms에서 3ms는 배수로는 동일하게 10배차이지만 하나의 request의 한계 속도가 0.3s라고 했을때 하나의 request는 고작해야 10%(27ms)도 안되는 차이가 있을뿐이다. 그리고 10%의 효과를 얻기 위한 비용은 막대하다.

초단위의 튜닝은 상대적으로 난이도가 쉽지만 ms단위의 튜닝은 수만줄의 DB쿼리에 각각에 대해 비싼 비용의 전문가들이 작성해야 하고 이미 그전에 하드웨어 튜닝, OS 튜닝, 그리고 물리적인 설계에서부터 테이블과 인덱스 옵션 하나하나의 제어와 통제, 다중사용자 환경을 고려한 Process와 Latch 경합에 대한 고려도 필요하다. 그럴바에는 차라리 초급 프로그래머가 대충 만들수 있는 이미지 등의 리소스 캐쉬가 비용도 작고 효과도 더욱 좋다. 

CPU 설계같은 일이 아닌 일반 어플리케이션에서 한자리수 ms 단위의 억세스는 ROI상 그렇게 효과적이지 못하다면 극단적으로 통제를 해서 얻는 27ms를 별로 관심도 없는 속도 향상에 투자하는게 아니라 아니라 여기서 얻는 여유를 몽땅 다른곳으로 돌려버리면 어떨까? 라는 시덥잖은 생각에서 출발했다.

'Framework > Another Lore' 카테고리의 다른 글

AL : Workspace  (0) 2009.04.23
AL : Node  (0) 2009.04.23
AL : PropertyDefinition  (0) 2009.04.23
AL : NodeType  (0) 2009.04.23
AL : Lock  (0) 2009.04.23
Posted by bleujin
Framework/Another Lore2009. 4. 23. 11:08

Property Definitions(속성정의) :
A node type contains a set of definitions specifying the properties that nodes of this node type are allowed (or required) to have and the characteristics of those properties.  A property definition has all the attributes of a generic item definition as well as the following property-specific attributes:


PropertyType

A property definition must specify a property type. Type[String, Binary, Long, Double, Boolean, Date, Name, Text, Reference, Path, ViertualText, Undefined] : defines integer constants for the available property types as well as for their standardized type names (used in serialization) and two methods for converting back and forth between name and integer value:

[Example Code]
 GlobalStore globalStore = session.getGlobalStore() ;
 PropertyDefinition explain = globalStore.createPropertyDefinition(IDString.propId("explain"), Property.Type.STRING) ;
 assertEquals(explain.requiredType(), Property.Type.STRING) ;

 NodeType newType = globalStore.createNodeType(objectType, "temporaryType", new PropertyDefinition[]{explain}) ;
  MappedDefinition md = newType.getMappedDefinition(IDString.propId("explain")) ;
  assertEquals(md.requiredType(), Property.Type.STRING) ;
  assertEquals(1, newType.getMappedDefinitions().size()) ;
  assertEquals("explain", md.getId().getString()) ;




<Inteface PropertyDefinition extends Serializable>
public IDString getId();
public boolean isSameProperty(IDString propId);
public Property.Type requiredType();



PropertyDefinition은 Id와 PropertyType으로 구성되어 있다.


DB의 컬럼과 비슷하기는 하지만 컬럼과 달리 constraint와 defaultValue를 가지지 않는다. 이러한 것들은 NodeType과 Mapping되는 과정에서 추가되고 PropertyDefinition은 단지 해당 아이디를 가진 PropertyDefinition은 특정 Property.Type을 가진다고 정의할 뿐이다.


'Framework > Another Lore' 카테고리의 다른 글

AL : Node  (0) 2009.04.23
AL : Property  (0) 2009.04.23
AL : NodeType  (0) 2009.04.23
AL : Lock  (0) 2009.04.23
AL : sequence  (0) 2009.04.23
Posted by bleujin
Framework/Another Lore2009. 4. 23. 04:46


An important feature of many repositories is the ability to distinguish the entities stored in the repository by type.
Node types are used to enforce structural restrictions on the nodes and properties in a workspace by defining for each node, its required and permitted child nodes and properties.

Every node has one declared node type. node types are typically used to defined the core characteristics of
a node. In a writable repository a node's primary type is first assigned upon node creation.

Repository implementations may vary as to how flexible they are in allowing changes to the node types assigned to a node. Each repository has a single, system-wide registry of node types. Typically, a repository will come with some implementation-determined set of built-in node types.

Some of these types may be vendor-specific while others may be standard predefined node types defined. Some repositories may further allow users to register new node types programmatically


A node type definition consists of the following attributes:


NodeTypeId


Every registered node type has a name, unique within the repository. NodeTypeId consists of prefix of AnotherLore URI and short local name in a workspace




Supertypes


A node type has zero or one supertype. Supertypes are specified by name. The supertype relation is transitive: If T1 is a supertype of T2 and T2 is a supertype of T3 then T1 is a supertype of T3.



Property Definitions

A node type may contain a list of property definitions, which specify the properties that nodes of that type are permitted or required to have and the characteristics of those properties. The list of property definitions may be empty.


Orderable Member NodeType

A node type may declare its member type orderable, meaning that for all nodes of that type, the order that the member nodes are iterated over can be programmatically controlled by the user. A node type may contain a list of member node definitions, which specify the permitted or required child nodes and their characteristics. member type definitions may be empty. if not setted, in principle have objectType(objectType have no propertyDefinition and is supertype of all nodetype)


Mapping Property Definition

The default values attribute of a property definition defines the values assigned to property if it is auto-created. If the property is single-valued this attribute will hold a single value.

A property definition may impose constraints on the value that the property may hold. These value constraints are defined by an array of strings, whose format differs depending on the type of the property.


[example Code]

NodeType commentType = globalStore.createNodeType(objectType, "comment", new PropertyDefinition[] { comment, regUserId, regDate });
  commentType.setConstraint(COMMENT, new RangeByteLengthConstraint(10, 4000));
  commentType.setConstraint(REGUSERID, new RequireConstraint());
  commentType.setConstraint(REGDATE, new RequireConstraint());      // set constraint
  commentType.setDefaultValue(REGDATE, new CurrentDateValue()) ;  // set default



<Inteface NodeType extends Serializable, Publishable>

 public NodeType setConstraint(IDString propId, IValueConstraint constraint)  throws RepositoryException ;
 public NodeType setDefaultValue(IDString propId, IDefaultValue defaultValue)  throws RepositoryException ;
 public NodeTypeId getId() ;
 public NodeType getSuperType()  throws RepositoryException ;
 public void setMemberType(NodeType memberType)  throws RepositoryException ; 
 public NodeType getMemberType()  throws RepositoryException ;
 public boolean isAllowedMemberType(NodeType memberType)  throws RepositoryException ;
 public GenericIterator<MappedDefinition> getMappedDefinitions() throws RepositoryException;
 public MappedDefinition getMappedDefinition(IDString propId) throws RepositoryException ;
 public boolean containsPropId(IDString propId)  throws RepositoryException ;
 public boolean isNodeType(NodeType thatType)  throws RepositoryException ;
 public boolean isNodeType(String thatTypeId)  throws RepositoryException ;
 public GenericIterator<ValidMessage> getViolateConstraintMessage(Node node) throws RepositoryException;




'Framework > Another Lore' 카테고리의 다른 글

AL : Property  (0) 2009.04.23
AL : PropertyDefinition  (0) 2009.04.23
AL : Lock  (0) 2009.04.23
AL : sequence  (0) 2009.04.23
자동화 테스트 - 자바스크립트  (0) 2009.04.17
Posted by bleujin
Framework/Another Lore2009. 4. 23. 03:14


A lock is placed on a node by calling node.lock(boolean isDeep). The node on which a lock is placed is called the holding node
of that lock.


Shallow and Deep Locks

A lock can be specified as either shallow or deep. A shallow lock applies only holding node and its properties. A deep lock applies to its holding node and descendants. Consequently, there is a distinction between a lock being held
node and a lock applying to a node. A lock always applies to its holding node. However, if it is a deep lock, it also applies to all nodes in the holding node's subgraph. When a lock applies to a node, that node is said to be locked.

Lock Owner

Initially, the session through which a lock is placed is the owner of that lock. This means the session has the power to alter the locked node and to remove the lock.

Repositories may support client-specified lock owner information.


Placing and Removing a Lock

When a lock is placed on a node, it can be specified to be either a session-scoped lock. A session-scoped lock automatically expires when the session through which the lock owner placed the lock expires. or can is released througt explicitly unlocked


Getting a Lock

node.getLock() returns the Lock object that applies to the node. if session is not same, lock cant called unlock()

void lock.unlock()
Removes the lock


Testing for Lock Holding

boolean node.isLocked()

returns true if the node holds a lock; otherwise returns false. To hold a lock means that the node has actually had a lock placed on it specifically, as opposed to having a lock apply to it due to a deep lock held by a node above.


Lock Object

The Lock object represents a lock on a particular node. It is acquired either on lock creation through node.getLock() 


[Example Code]
  Node parent = session.createNode(root, objectType, "parent");
  Node child = session.createNode(parent, objectType, "child");
  boolean isDeep = true;
  parent.lock(isDeep);
  assertTrue(child.isLocked());
  assertTrue(parent.isLocked());

  Lock lock = parent.getLock() ;
  lock.unlock();
  assertFalse(child.isLocked());
  assertFalse(parent.isLocked());



<Inteface Lock>
 public Node getNode() throws RepositoryException; // Getting the Lock Holding Node
 public boolean isDeep(); // Testing Lock Depth
 public void unlock() throws RepositoryException, InterruptedException;
 public boolean isOwner(UserBean user); // Testing Lock Owning Session


LockException

When a method fails due to the presence or absence of a lock on a particular node a LockException is thrown.
LockException extends RepositoryException,


종종 내가 특정 노드를 수정하고 있을때 (즉 edit Mode일때) 다른 누군가가 수정을 할 수 없게 하고 싶을 경우가 있다.
그러나 잘 알다시피 웹은 디스커넥트 환경이기 때문에 이러한 Lock의 구현이 조금 다르다.

첫번째로 했던 방법은 editMode로 갈때 userId와 node.UUID를 특정 테이블에 표시를 해두는 방식이다. 그리고 editMode로 들어가기 전에 이런 표시가 있는지 살펴보는 뻔한 방식을 생각해 볼 수 있다. 이 방법의 단점은 첫번째 사용자가 editMode에서 다른 사이트로 가버리거나 브라우저를 끄고 놀러가버렸을 때이다.

적절한 process를 거쳐 취소나 저장을 누르지 않은 상태에서 다른 곳으로 가버렸기 때문에 먼저 마크했던 정보가 여전히 남아있어서 다른 사용자는 여전히 접근할 수 었다는 단점이 있다. 관리자가 수동으로 풀어주는 것도 한두번이다.

두번째의 문제는 editMode로 갈때와 viewMode일때의 구분이 어렵다는 것이다. 모두 select이며 이걸 viewMode로 쓸지 editMode로 쓸지는 전적으로 프로그램에게 달려 있다.

또 다른 문제는 만약 setProperty시 Lock을 건다고 해도 commit까지의 시간이 아주 짧아(ns 레벨) 효과가 아주 미비하고 추가적으로 관리해야 할  Lock정보가 너무 많아지는 문제가 있다.



그래서 AL에서는 DB에서 사용하는 Lock 개념과 좀 달리 접근할 필요가 있었다.

첫번째 DB등의 persitence에 마킹하는 방법은 사용자가 다른 곳으로 가버렸을때나 AL의 restart등에도 남아있기 때문에 Lock 정보는 메모리에서 관리한다.

두번째 최소한의 정보를 관리하기 위해 Lock은 별도로 호출하기 전까지는 자동으로 걸리지 않는다.

세번째 Lock은 얼마의 시간이 지나면 자동으로 release 된다.



첫번째와 세번째 조건을 만족하기 위한 가장 좋은 장소는 Session이다. http Session이 아니라 AL은 웹과 상관없이 자체적으로 Session을 관리한다. session은 일정 시간이 지나도록 행동이 없으면 자동으로 invalidate 되기 때문에 위 조건을 만족할 수 있다.

두번째로는 node.lock() 을 호출하는 순간에만 lock이 걸린다. lock이 걸린다고 setProperty를 호출 할 수 없는 것은 아니다. 다만 node.isLocked에서 반환되는 값이 달라질 뿐이다.

명시적으로 해제하고 싶으면 해당 Owner가 node.getLock().unlock()를 호출해야 한다. 추가적으로 lock escalation으로 관리해야 할 락을 줄인다.


'Framework > Another Lore' 카테고리의 다른 글

AL : PropertyDefinition  (0) 2009.04.23
AL : NodeType  (0) 2009.04.23
AL : sequence  (0) 2009.04.23
자동화 테스트 - 자바스크립트  (0) 2009.04.17
와~~~  (0) 2009.04.09
Posted by bleujin
Framework/Another Lore2009. 4. 23. 02:05


[Example Code]
  DatabaseSequence dseq = new DatabaseSequence(session.getGlobalStore().getNamespace(), DBController.getTestInstance()) ;
  Sequence seq = new CacheSequence(dseq) ;
  
  IDString seqId = IDString.seqId("seq_name") ;
  seq.reset(seqId) ;
  
  long i = seq.nextVal(seqId) ;
  assertEquals(1, i) ;

  assertEquals(1L, seq.currVal(seqId)) ;
  assertEquals(2L, seq.nextVal(seqId)) ;
  assertEquals(3L, seq.nextVal(seqId)) ;
  
  seq.reset(seqId) ;
  assertEquals(0, seq.currVal(seqId)) ;
  for (int j = 1; j < 111; j++) {
   assertEquals(1L *j, seq.nextVal(seqId)) ;
   assertEquals(1L *j, seq.currVal(seqId)) ;
  }

<interface Sequence>
 public void reset(IDString seqName);
 public long currVal(IDString seqName);
 public long nextVal(IDString seqName);


적합한 PK의 역할을 할 수 있는 컬럼(들)이 없거나 그 컬럼(들)의 length가 길때 Sequecne는 꽤 유용하다.

다만 DB의 sequence는 몇가지 불편한 점이 있다.
이를테면 parent - child 구조의 data를 하나의 트랜잭션으로 입력하려고 할때 parent의 PK가 sequence 라면

parent가 입력되기 전까지 sequence를 모른다.
child는 parent의 PK를 알아야 입력이 가능하다. 라는 명제가 충돌한다.

그나마 오라클 같은 경우 sequence는 별도의 세그먼트 이기 때문에

현재 사용하는 sequence의 next value를 알아와서 그 값으로 parent와 child의 값을 설정한후 입력한다.
가 가능하지만..

다른 DB의 경우 이를테면 MSSQL의 경우 seq는 table에 IDENTITY형으로 종속되어 있기 때문에
하나의 트랜잭션으로 유지하기 위해 꽤 번거로운 짓을 많이 해야 한다.


AL에서는 그래서 별도의 Sequence 객체를 제공하는데.
원리는 간단하다. key, value 의 하나의 테이블을 만들고 key에는 sequnceName을 value에는 currValue를 저장하고 nextValue를 요청했을 경우 value+1로 업데이트 하고 해당 value를 던져주면 된다. 이렇게 구현할 경우 필요할 경우 sequece를 아주 간단하게 만들수 있다. 

예컨데 이런 SQL 이다. 

    function reset(v_seqName varchar2)
    return number
    is
    begin
        update sequence_tblc set seqValue = 0 where seqName = v_seqName ;
   
        return SQL%ROWCOUNT ;
    end ;

    function currVal(v_seqName varchar2)
    return number
    is
        v_result number ;
    begin
        select NVL(max(seqValue), -1) into v_result from sequence_tblc where seqName = v_seqName ;
       
        If v_result = -1 then
            insert into sequence_tblc(seqName, seqValue) values(v_seqName, 0) ;
            return 0 ;
       
        end if ;
        return v_result ;
    end ;
   
    function nextVal(v_seqName varchar2)
    return number
    is
        v_result number ;
    begin
        update sequence_tblc set seqValue = seqValue+1 where seqName = v_seqName returning seqValue into v_result;
       
        IF SQL%ROWCOUNT = 0 Then
            insert into sequence_tblc(seqName, seqValue) values(v_seqName, 1) ;
            return 1 ;
        End if ;
       
        return v_result ;
    end ;

다만 이렇게 사용하면 Sequence를 사용하기는 매우 편하지만 nextVal을 호출할때마다 DB Call이 일어난다. 그리고 Table update 방식은 DB sequence 방식보다 무겁다.

AL에서는 그래서 DB의 sequnce의 아이디어를 그대로 사용한다. AL.Sequnece의 nextVal을 호출하면 실제로는 +20으로 업데이트하고 20개의 값을 queue에 저장해둔다. queue가 비어 있지 않으면 queue값을 poll 해주고 20개를 모두 소모해 비어 있으면 다시 +20을 해주고 반복한다.

'Framework > Another Lore' 카테고리의 다른 글

AL : NodeType  (0) 2009.04.23
AL : Lock  (0) 2009.04.23
자동화 테스트 - 자바스크립트  (0) 2009.04.17
와~~~  (0) 2009.04.09
AL - Extreme  (0) 2009.04.04
Posted by bleujin