Skip to content

zenstack generate JavaScript heap out of memory (v2 alpha) #1064

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
andrewkucz opened this issue Feb 28, 2024 · 2 comments
Closed

zenstack generate JavaScript heap out of memory (v2 alpha) #1064

andrewkucz opened this issue Feb 28, 2024 · 2 comments

Comments

@andrewkucz
Copy link

Description and expected behavior

Hi again, I have continued playing with the v2 alpha and may have run into an accidental stress test causing a crash due to heap memory.

I will post my zschema below. It is perhaps too big or too complex with all the relations / polymorphic relationships and may be an edge case, but I just wanted to bring this to your attention.

Prisma and zod schemas generate successfully but the PrismaClient enhancer does not. It runs for a few minutes and then crashes with the output shown. I have tried removing various combinations of models and I could get it to work sometimes, but with this schema, I was able to reproduce a crash each time.

Output:

> zenstack generate

⌛️ ZenStack CLI v2.0.0-alpha.1, running plugins
✔ Generating Prisma schema
✔ Generating Zod schemas
⠙ Generating PrismaClient enhancer
<--- Last few GCs --->

[33877:0x140008000]   298372 ms: Mark-Compact (reduce) 4082.3 (4107.0) -> 4082.3 (4104.0) MB, 41.79 / 0.00 ms  (average mu = 0.128, current mu = 0.000) last resort; GC in old space requested
[33877:0x140008000]   298416 ms: Mark-Compact (reduce) 4082.3 (4104.0) -> 4082.3 (4104.0) MB, 43.46 / 0.00 ms  (average mu = 0.067, current mu = 0.001) last resort; GC in old space requested


<--- JS stacktrace --->

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
----- Native stack trace -----

 1: 0x10491553c node::Abort() [/Users/andrew/.nvm/versions/node/v20.11.0/bin/node]
 2: 0x10491573c node::ModifyCodeGenerationFromStrings(v8::Local<v8::Context>, v8::Local<v8::Value>, bool) [/Users/andrew/.nvm/versions/node/v20.11.0/bin/node]
 3: 0x104a9a6c4 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, v8::OOMDetails const&) [/Users/andrew/.nvm/versions/node/v20.11.0/bin/node]
 4: 0x104c65194 v8::internal::MemoryController<v8::internal::V8HeapTrait>::MinimumAllocationLimitGrowingStep(v8::internal::Heap::HeapGrowingMode) [/Users/andrew/.nvm/versions/node/v20.11.0/bin/node]
 5: 0x104c49240 v8::internal::Factory::AllocateRaw(int, v8::internal::AllocationType, v8::internal::AllocationAlignment) [/Users/andrew/.nvm/versions/node/v20.11.0/bin/node]
datasource db {
    provider = 'sqlite'
    url = 'file:./dev.db'
}

generator client {
    provider = "prisma-client-js"
}

model Account {
    id                String  @id @default(cuid())
    userId            String
    type              String
    provider          String
    providerAccountId String
    refresh_token     String? // @db.Text
    access_token      String? // @db.Text
    expires_at        Int?
    token_type        String?
    scope             String?
    id_token          String? // @db.Text
    session_state     String?
    user              User    @relation(fields: [userId], references: [id], onDelete: Cascade)
    @@allow('all', auth().id == userId)
    @@unique([provider, providerAccountId])
}

model Session {
    id           String   @id @default(cuid())
    sessionToken String   @unique
    userId       String
    expires      DateTime
    user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)
    @@allow('all', auth().id == userId)
}

model VerificationToken {
    identifier String
    token      String   @unique
    expires    DateTime
    
    @@allow('all', true)
    @@unique([identifier, token])
}

model User {
    id            String    @id @default(cuid())
    name          String?
    email         String?   @unique
    emailVerified DateTime?
    image         String
    accounts      Account[]
    sessions      Session[]

    username    String    @unique @length(min: 4, max: 20)
    about       String?   @length(max: 500)
    location    String?   @length(max: 100)

    role        String @default("USER") @deny(operation: "update", auth().role != "ADMIN")

    inserted_at DateTime   @default(now())
    updated_at  DateTime   @updatedAt() @default(now())

    editComments  EditComment[]

    posts       Post[]
    rankings                UserRanking[]
    ratings                 UserRating[]
    favorites               UserFavorite[]

    people        Person[]
    studios       Studio[]
    edits         Edit[]
    attachments Attachment[]
    galleries Gallery[]

    uploads UserUpload[]

    maxUploadsPerDay Int @default(10)
    maxEditsPerDay Int @default(10)

    // everyone can signup, and user profile is also publicly readable
    @@allow('create,read', true)
    // only the user can update or delete their own profile
    @@allow('update,delete', auth() == this)
}

abstract model UserEntityRelation {
  entityId      String?
  entity        Entity?        @relation(fields: [entityId], references: [id], onUpdate: NoAction)
  userId   String
  user     User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: NoAction)


  // everyone can read
  @@allow('read', true)
  @@allow('create,update,delete', auth().id == this.userId)

  @@unique([userId,entityId])
}

model UserUpload {
    
    timestamp DateTime @default(now())
    
    key String @id
    url String @unique
    size Int

    userId String  
    user   User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: NoAction)

    @@allow('create', auth().id == userId)
    @@allow('all', auth().role == "ADMIN")
}

model Post {
  id      Int    @id @default(autoincrement())
  title    String @length(max: 100)
  body     String @length(max: 1000)
  createdAt DateTime @default(now())

  authorId String  
  author   User @relation(fields: [authorId], references: [id], onDelete: Cascade, onUpdate: NoAction)
  
  @@allow('read', true)
  @@allow('create,update,delete', auth().id == authorId && auth().role == "ADMIN")
}

model Edit extends UserEntityRelation {

  id      String          @id @default(cuid())
  status String @default("PENDING") @allow('update', auth().role in ["ADMIN", "MODERATOR"])
  type    String  @allow('update', false)
  timestamp DateTime   @default(now())
  note  String? @length(max: 300)
  // for creates - createPayload & updates - data before diff is applied
  data String?
  // for updates
  diff String?

  comments EditComment[]

}

model EditComment {
  id      Int          @id @default(autoincrement())
  timestamp DateTime @default(now())
  content  String  @length(max: 300)
  editId  String
  edit Edit @relation(fields: [editId], references: [id], onUpdate: Cascade)
  authorId  String
  author    User @relation(fields: [authorId], references: [id], onUpdate: Cascade)

  // everyone can read
  @@allow('read', true)
  @@allow('create,update,delete', auth().id == this.authorId || auth().role in ["ADMIN", "MODERATOR"])
}

model MetadataIdentifier {

  id  Int @default(autoincrement()) @id
  
  identifier String

  metadataSource String
  MetadataSource MetadataSource @relation(fields: [metadataSource], references: [slug], onUpdate: Cascade)

  entities Entity[]

  @@unique([identifier, metadataSource])

  @@allow('read', true)
  @@allow('create,update,delete', auth().role in ["ADMIN", "MODERATOR"])

}
model MetadataSource {
  slug String @id
  name String @unique
  identifierRegex String
  desc   String?
  url    String
  icon   String
  identifiers MetadataIdentifier[]

  @@allow('all', auth().role == "ADMIN")
}

model Attachment extends UserEntityRelation {
  id            String         @id @default(cuid())
  createdAt     DateTime   @default(now())
  key           String         @unique
  url           String          @unique
  galleries     Gallery[]
  @@allow('delete', auth().role in ["ADMIN", "MODERATOR"])
}

model Entity {

  id      String          @id @default(cuid()) 
  name    String
  desc  String?          
  
  attachments Attachment[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt @default(now())
  
  type String

  status  String @default("PENDING") // PENDING ON INITIAL CREATION
  verified Boolean @default(false)

  edits Edit[]  
  userRankings UserRanking[]
  userFavorites UserFavorite[]
  userRatings UserRating[]
  metaIdentifiers MetadataIdentifier[]

  @@delegate(type)
 
  @@allow('read', true)
  @@allow('create', auth() != null)
  @@allow('update', auth().role in ["ADMIN", "MODERATOR"])
  @@allow('delete', auth().role == "ADMIN")
}

model Person extends Entity {
  studios Studio[]
  owners    User[]
  clips     Clip[]
  events Event[]
  galleries Gallery[]
}

model Studio extends Entity {
  people Person[]
  owners User[]
  clips     Clip[]
  events Event[]
  galleries Gallery[]
}

model Clip extends Entity {
  url   String?
  people Person[]
  studios Studio[]
  galleries Gallery[]
}

model UserRanking extends UserEntityRelation {
  id      String       @id @default(cuid()) 
  rank     Int  @gte(1) @lte(100)
  note     String? @length(max: 300)
}

model UserFavorite extends UserEntityRelation {
  id      String       @id @default(cuid()) 
  favoritedAt DateTime @default(now())
}

model UserRating  extends UserEntityRelation  {
  id      String @id @default(cuid()) 
  rating    Int @gte(1) @lte(5)
  note     String?  @length(max: 500)  
  ratedAt DateTime @default(now())
}

model Event {
  id      Int       @id @default(autoincrement()) 
  name    String  @length(max: 100)  
  desc    String?  @length(max: 500)  
  location String?  @length(max: 100) 
  date     DateTime?
  people   Person[]
  studios  Studio[]

  @@allow('read', true)
  @@allow('create,update,delete', auth().role == "ADMIN")
}

model Gallery {
  id      String       @id @default(cuid()) 
  studioId String?  
  personId String?  
  timestamp DateTime  @default(now())
  authorId   String   
  author   User @relation(fields: [authorId], references: [id], onDelete: Cascade, onUpdate: NoAction)
  people    Person[]
  studios   Studio[]
  clips     Clip[]
  attachments Attachment[]

  @@allow('read', true)
  @@allow('create,update,delete', auth().id == this.authorId && auth().role == "ADMIN")
}

Environment (please complete the following information):

  • ZenStack version: 2.0.0-alpha.1
  • Prisma version: 5.10.2
  • Database type: sqlite (trying temporarily to use sqlite due to my previous issue preventing me from using postgres)
  • Tested on M1 MBP & Win11 Desktop - same result
  • Tested on Node 20 and Node 18 - same result
@ymc9
Copy link
Member

ymc9 commented Mar 1, 2024

Thanks for reporting this @andrewkucz , I'll debug and see what's going wrong.

@ymc9
Copy link
Member

ymc9 commented Mar 9, 2024

Fixed in 2.0.0-alpha.6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants